Edit on GitHub

sqlglot.generator

   1from __future__ import annotations
   2
   3import logging
   4import re
   5import typing as t
   6from collections import defaultdict
   7from functools import reduce, wraps
   8
   9from sqlglot import exp
  10from sqlglot.errors import ErrorLevel, UnsupportedError, concat_messages
  11from sqlglot.helper import apply_index_offset, csv, name_sequence, seq_get
  12from sqlglot.jsonpath import ALL_JSON_PATH_PARTS, JSON_PATH_PART_TRANSFORMS
  13from sqlglot.time import format_time
  14from sqlglot.tokens import TokenType
  15
  16if t.TYPE_CHECKING:
  17    from sqlglot._typing import E
  18    from sqlglot.dialects.dialect import DialectType
  19
  20    G = t.TypeVar("G", bound="Generator")
  21    GeneratorMethod = t.Callable[[G, E], str]
  22
  23logger = logging.getLogger("sqlglot")
  24
  25ESCAPED_UNICODE_RE = re.compile(r"\\(\d+)")
  26UNSUPPORTED_TEMPLATE = "Argument '{}' is not supported for expression '{}' when targeting {}."
  27
  28
  29def unsupported_args(
  30    *args: t.Union[str, t.Tuple[str, str]],
  31) -> t.Callable[[GeneratorMethod], GeneratorMethod]:
  32    """
  33    Decorator that can be used to mark certain args of an `Expression` subclass as unsupported.
  34    It expects a sequence of argument names or pairs of the form (argument_name, diagnostic_msg).
  35    """
  36    diagnostic_by_arg: t.Dict[str, t.Optional[str]] = {}
  37    for arg in args:
  38        if isinstance(arg, str):
  39            diagnostic_by_arg[arg] = None
  40        else:
  41            diagnostic_by_arg[arg[0]] = arg[1]
  42
  43    def decorator(func: GeneratorMethod) -> GeneratorMethod:
  44        @wraps(func)
  45        def _func(generator: G, expression: E) -> str:
  46            expression_name = expression.__class__.__name__
  47            dialect_name = generator.dialect.__class__.__name__
  48
  49            for arg_name, diagnostic in diagnostic_by_arg.items():
  50                if expression.args.get(arg_name):
  51                    diagnostic = diagnostic or UNSUPPORTED_TEMPLATE.format(
  52                        arg_name, expression_name, dialect_name
  53                    )
  54                    generator.unsupported(diagnostic)
  55
  56            return func(generator, expression)
  57
  58        return _func
  59
  60    return decorator
  61
  62
  63class _Generator(type):
  64    def __new__(cls, clsname, bases, attrs):
  65        klass = super().__new__(cls, clsname, bases, attrs)
  66
  67        # Remove transforms that correspond to unsupported JSONPathPart expressions
  68        for part in ALL_JSON_PATH_PARTS - klass.SUPPORTED_JSON_PATH_PARTS:
  69            klass.TRANSFORMS.pop(part, None)
  70
  71        return klass
  72
  73
  74class Generator(metaclass=_Generator):
  75    """
  76    Generator converts a given syntax tree to the corresponding SQL string.
  77
  78    Args:
  79        pretty: Whether to format the produced SQL string.
  80            Default: False.
  81        identify: Determines when an identifier should be quoted. Possible values are:
  82            False (default): Never quote, except in cases where it's mandatory by the dialect.
  83            True or 'always': Always quote.
  84            'safe': Only quote identifiers that are case insensitive.
  85        normalize: Whether to normalize identifiers to lowercase.
  86            Default: False.
  87        pad: The pad size in a formatted string. For example, this affects the indentation of
  88            a projection in a query, relative to its nesting level.
  89            Default: 2.
  90        indent: The indentation size in a formatted string. For example, this affects the
  91            indentation of subqueries and filters under a `WHERE` clause.
  92            Default: 2.
  93        normalize_functions: How to normalize function names. Possible values are:
  94            "upper" or True (default): Convert names to uppercase.
  95            "lower": Convert names to lowercase.
  96            False: Disables function name normalization.
  97        unsupported_level: Determines the generator's behavior when it encounters unsupported expressions.
  98            Default ErrorLevel.WARN.
  99        max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError.
 100            This is only relevant if unsupported_level is ErrorLevel.RAISE.
 101            Default: 3
 102        leading_comma: Whether the comma is leading or trailing in select expressions.
 103            This is only relevant when generating in pretty mode.
 104            Default: False
 105        max_text_width: The max number of characters in a segment before creating new lines in pretty mode.
 106            The default is on the smaller end because the length only represents a segment and not the true
 107            line length.
 108            Default: 80
 109        comments: Whether to preserve comments in the output SQL code.
 110            Default: True
 111    """
 112
 113    TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = {
 114        **JSON_PATH_PART_TRANSFORMS,
 115        exp.AllowedValuesProperty: lambda self,
 116        e: f"ALLOWED_VALUES {self.expressions(e, flat=True)}",
 117        exp.AnalyzeColumns: lambda self, e: self.sql(e, "this"),
 118        exp.AnalyzeWith: lambda self, e: self.expressions(e, prefix="WITH ", sep=" "),
 119        exp.ArrayContainsAll: lambda self, e: self.binary(e, "@>"),
 120        exp.ArrayOverlaps: lambda self, e: self.binary(e, "&&"),
 121        exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}",
 122        exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}",
 123        exp.CaseSpecificColumnConstraint: lambda _,
 124        e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
 125        exp.Ceil: lambda self, e: self.ceil_floor(e),
 126        exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
 127        exp.CharacterSetProperty: lambda self,
 128        e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
 129        exp.ClusteredColumnConstraint: lambda self,
 130        e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})",
 131        exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
 132        exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
 133        exp.ConnectByRoot: lambda self, e: f"CONNECT_BY_ROOT {self.sql(e, 'this')}",
 134        exp.ConvertToCharset: lambda self, e: self.func(
 135            "CONVERT", e.this, e.args["dest"], e.args.get("source")
 136        ),
 137        exp.CopyGrantsProperty: lambda *_: "COPY GRANTS",
 138        exp.CredentialsProperty: lambda self,
 139        e: f"CREDENTIALS=({self.expressions(e, 'expressions', sep=' ')})",
 140        exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
 141        exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
 142        exp.DynamicProperty: lambda *_: "DYNAMIC",
 143        exp.EmptyProperty: lambda *_: "EMPTY",
 144        exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
 145        exp.EnviromentProperty: lambda self, e: f"ENVIRONMENT ({self.expressions(e, flat=True)})",
 146        exp.EphemeralColumnConstraint: lambda self,
 147        e: f"EPHEMERAL{(' ' + self.sql(e, 'this')) if e.this else ''}",
 148        exp.ExcludeColumnConstraint: lambda self, e: f"EXCLUDE {self.sql(e, 'this').lstrip()}",
 149        exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
 150        exp.Except: lambda self, e: self.set_operations(e),
 151        exp.ExternalProperty: lambda *_: "EXTERNAL",
 152        exp.Floor: lambda self, e: self.ceil_floor(e),
 153        exp.Get: lambda self, e: self.get_put_sql(e),
 154        exp.GlobalProperty: lambda *_: "GLOBAL",
 155        exp.HeapProperty: lambda *_: "HEAP",
 156        exp.IcebergProperty: lambda *_: "ICEBERG",
 157        exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})",
 158        exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
 159        exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}",
 160        exp.Intersect: lambda self, e: self.set_operations(e),
 161        exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}",
 162        exp.Int64: lambda self, e: self.sql(exp.cast(e.this, exp.DataType.Type.BIGINT)),
 163        exp.LanguageProperty: lambda self, e: self.naked_property(e),
 164        exp.LocationProperty: lambda self, e: self.naked_property(e),
 165        exp.LogProperty: lambda _, e: f"{'NO ' if e.args.get('no') else ''}LOG",
 166        exp.MaterializedProperty: lambda *_: "MATERIALIZED",
 167        exp.NonClusteredColumnConstraint: lambda self,
 168        e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})",
 169        exp.NoPrimaryIndexProperty: lambda *_: "NO PRIMARY INDEX",
 170        exp.NotForReplicationColumnConstraint: lambda *_: "NOT FOR REPLICATION",
 171        exp.OnCommitProperty: lambda _,
 172        e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS",
 173        exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}",
 174        exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}",
 175        exp.Operator: lambda self, e: self.binary(e, ""),  # The operator is produced in `binary`
 176        exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}",
 177        exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
 178        exp.PartitionedByBucket: lambda self, e: self.func("BUCKET", e.this, e.expression),
 179        exp.PartitionByTruncate: lambda self, e: self.func("TRUNCATE", e.this, e.expression),
 180        exp.PivotAny: lambda self, e: f"ANY{self.sql(e, 'this')}",
 181        exp.PositionalColumn: lambda self, e: f"#{self.sql(e, 'this')}",
 182        exp.ProjectionPolicyColumnConstraint: lambda self,
 183        e: f"PROJECTION POLICY {self.sql(e, 'this')}",
 184        exp.Put: lambda self, e: self.get_put_sql(e),
 185        exp.RemoteWithConnectionModelProperty: lambda self,
 186        e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}",
 187        exp.ReturnsProperty: lambda self, e: (
 188            "RETURNS NULL ON NULL INPUT" if e.args.get("null") else self.naked_property(e)
 189        ),
 190        exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}",
 191        exp.SecureProperty: lambda *_: "SECURE",
 192        exp.SecurityProperty: lambda self, e: f"SECURITY {self.sql(e, 'this')}",
 193        exp.SetConfigProperty: lambda self, e: self.sql(e, "this"),
 194        exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
 195        exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}",
 196        exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}",
 197        exp.SqlReadWriteProperty: lambda _, e: e.name,
 198        exp.SqlSecurityProperty: lambda _,
 199        e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
 200        exp.StabilityProperty: lambda _, e: e.name,
 201        exp.Stream: lambda self, e: f"STREAM {self.sql(e, 'this')}",
 202        exp.StreamingTableProperty: lambda *_: "STREAMING",
 203        exp.StrictProperty: lambda *_: "STRICT",
 204        exp.SwapTable: lambda self, e: f"SWAP WITH {self.sql(e, 'this')}",
 205        exp.TableColumn: lambda self, e: self.sql(e.this),
 206        exp.Tags: lambda self, e: f"TAG ({self.expressions(e, flat=True)})",
 207        exp.TemporaryProperty: lambda *_: "TEMPORARY",
 208        exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
 209        exp.ToMap: lambda self, e: f"MAP {self.sql(e, 'this')}",
 210        exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}",
 211        exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions),
 212        exp.TransientProperty: lambda *_: "TRANSIENT",
 213        exp.Union: lambda self, e: self.set_operations(e),
 214        exp.UnloggedProperty: lambda *_: "UNLOGGED",
 215        exp.UsingTemplateProperty: lambda self, e: f"USING TEMPLATE {self.sql(e, 'this')}",
 216        exp.UsingData: lambda self, e: f"USING DATA {self.sql(e, 'this')}",
 217        exp.Uuid: lambda *_: "UUID()",
 218        exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE",
 219        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
 220        exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}",
 221        exp.VolatileProperty: lambda *_: "VOLATILE",
 222        exp.WeekStart: lambda self, e: f"WEEK({self.sql(e, 'this')})",
 223        exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
 224        exp.WithProcedureOptions: lambda self, e: f"WITH {self.expressions(e, flat=True)}",
 225        exp.WithSchemaBindingProperty: lambda self, e: f"WITH SCHEMA {self.sql(e, 'this')}",
 226        exp.WithOperator: lambda self, e: f"{self.sql(e, 'this')} WITH {self.sql(e, 'op')}",
 227        exp.ForceProperty: lambda *_: "FORCE",
 228    }
 229
 230    # Whether null ordering is supported in order by
 231    # True: Full Support, None: No support, False: No support for certain cases
 232    # such as window specifications, aggregate functions etc
 233    NULL_ORDERING_SUPPORTED: t.Optional[bool] = True
 234
 235    # Whether ignore nulls is inside the agg or outside.
 236    # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER
 237    IGNORE_NULLS_IN_FUNC = False
 238
 239    # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
 240    LOCKING_READS_SUPPORTED = False
 241
 242    # Whether the EXCEPT and INTERSECT operations can return duplicates
 243    EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = True
 244
 245    # Wrap derived values in parens, usually standard but spark doesn't support it
 246    WRAP_DERIVED_VALUES = True
 247
 248    # Whether create function uses an AS before the RETURN
 249    CREATE_FUNCTION_RETURN_AS = True
 250
 251    # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed
 252    MATCHED_BY_SOURCE = True
 253
 254    # Whether the INTERVAL expression works only with values like '1 day'
 255    SINGLE_STRING_INTERVAL = False
 256
 257    # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs
 258    INTERVAL_ALLOWS_PLURAL_FORM = True
 259
 260    # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH")
 261    LIMIT_FETCH = "ALL"
 262
 263    # Whether limit and fetch allows expresions or just limits
 264    LIMIT_ONLY_LITERALS = False
 265
 266    # Whether a table is allowed to be renamed with a db
 267    RENAME_TABLE_WITH_DB = True
 268
 269    # The separator for grouping sets and rollups
 270    GROUPINGS_SEP = ","
 271
 272    # The string used for creating an index on a table
 273    INDEX_ON = "ON"
 274
 275    # Whether join hints should be generated
 276    JOIN_HINTS = True
 277
 278    # Whether table hints should be generated
 279    TABLE_HINTS = True
 280
 281    # Whether query hints should be generated
 282    QUERY_HINTS = True
 283
 284    # What kind of separator to use for query hints
 285    QUERY_HINT_SEP = ", "
 286
 287    # Whether comparing against booleans (e.g. x IS TRUE) is supported
 288    IS_BOOL_ALLOWED = True
 289
 290    # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement
 291    DUPLICATE_KEY_UPDATE_WITH_SET = True
 292
 293    # Whether to generate the limit as TOP <value> instead of LIMIT <value>
 294    LIMIT_IS_TOP = False
 295
 296    # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ...
 297    RETURNING_END = True
 298
 299    # Whether to generate an unquoted value for EXTRACT's date part argument
 300    EXTRACT_ALLOWS_QUOTES = True
 301
 302    # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax
 303    TZ_TO_WITH_TIME_ZONE = False
 304
 305    # Whether the NVL2 function is supported
 306    NVL2_SUPPORTED = True
 307
 308    # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax
 309    SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE")
 310
 311    # Whether VALUES statements can be used as derived tables.
 312    # MySQL 5 and Redshift do not allow this, so when False, it will convert
 313    # SELECT * VALUES into SELECT UNION
 314    VALUES_AS_TABLE = True
 315
 316    # Whether the word COLUMN is included when adding a column with ALTER TABLE
 317    ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
 318
 319    # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
 320    UNNEST_WITH_ORDINALITY = True
 321
 322    # Whether FILTER (WHERE cond) can be used for conditional aggregation
 323    AGGREGATE_FILTER_SUPPORTED = True
 324
 325    # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds
 326    SEMI_ANTI_JOIN_WITH_SIDE = True
 327
 328    # Whether to include the type of a computed column in the CREATE DDL
 329    COMPUTED_COLUMN_WITH_TYPE = True
 330
 331    # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY
 332    SUPPORTS_TABLE_COPY = True
 333
 334    # Whether parentheses are required around the table sample's expression
 335    TABLESAMPLE_REQUIRES_PARENS = True
 336
 337    # Whether a table sample clause's size needs to be followed by the ROWS keyword
 338    TABLESAMPLE_SIZE_IS_ROWS = True
 339
 340    # The keyword(s) to use when generating a sample clause
 341    TABLESAMPLE_KEYWORDS = "TABLESAMPLE"
 342
 343    # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI
 344    TABLESAMPLE_WITH_METHOD = True
 345
 346    # The keyword to use when specifying the seed of a sample clause
 347    TABLESAMPLE_SEED_KEYWORD = "SEED"
 348
 349    # Whether COLLATE is a function instead of a binary operator
 350    COLLATE_IS_FUNC = False
 351
 352    # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle)
 353    DATA_TYPE_SPECIFIERS_ALLOWED = False
 354
 355    # Whether conditions require booleans WHERE x = 0 vs WHERE x
 356    ENSURE_BOOLS = False
 357
 358    # Whether the "RECURSIVE" keyword is required when defining recursive CTEs
 359    CTE_RECURSIVE_KEYWORD_REQUIRED = True
 360
 361    # Whether CONCAT requires >1 arguments
 362    SUPPORTS_SINGLE_ARG_CONCAT = True
 363
 364    # Whether LAST_DAY function supports a date part argument
 365    LAST_DAY_SUPPORTS_DATE_PART = True
 366
 367    # Whether named columns are allowed in table aliases
 368    SUPPORTS_TABLE_ALIAS_COLUMNS = True
 369
 370    # Whether UNPIVOT aliases are Identifiers (False means they're Literals)
 371    UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
 372
 373    # What delimiter to use for separating JSON key/value pairs
 374    JSON_KEY_VALUE_PAIR_SEP = ":"
 375
 376    # INSERT OVERWRITE TABLE x override
 377    INSERT_OVERWRITE = " OVERWRITE TABLE"
 378
 379    # Whether the SELECT .. INTO syntax is used instead of CTAS
 380    SUPPORTS_SELECT_INTO = False
 381
 382    # Whether UNLOGGED tables can be created
 383    SUPPORTS_UNLOGGED_TABLES = False
 384
 385    # Whether the CREATE TABLE LIKE statement is supported
 386    SUPPORTS_CREATE_TABLE_LIKE = True
 387
 388    # Whether the LikeProperty needs to be specified inside of the schema clause
 389    LIKE_PROPERTY_INSIDE_SCHEMA = False
 390
 391    # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be
 392    # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args
 393    MULTI_ARG_DISTINCT = True
 394
 395    # Whether the JSON extraction operators expect a value of type JSON
 396    JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
 397
 398    # Whether bracketed keys like ["foo"] are supported in JSON paths
 399    JSON_PATH_BRACKETED_KEY_SUPPORTED = True
 400
 401    # Whether to escape keys using single quotes in JSON paths
 402    JSON_PATH_SINGLE_QUOTE_ESCAPE = False
 403
 404    # The JSONPathPart expressions supported by this dialect
 405    SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy()
 406
 407    # Whether any(f(x) for x in array) can be implemented by this dialect
 408    CAN_IMPLEMENT_ARRAY_ANY = False
 409
 410    # Whether the function TO_NUMBER is supported
 411    SUPPORTS_TO_NUMBER = True
 412
 413    # Whether EXCLUDE in window specification is supported
 414    SUPPORTS_WINDOW_EXCLUDE = False
 415
 416    # Whether or not set op modifiers apply to the outer set op or select.
 417    # SELECT * FROM x UNION SELECT * FROM y LIMIT 1
 418    # True means limit 1 happens after the set op, False means it it happens on y.
 419    SET_OP_MODIFIERS = True
 420
 421    # Whether parameters from COPY statement are wrapped in parentheses
 422    COPY_PARAMS_ARE_WRAPPED = True
 423
 424    # Whether values of params are set with "=" token or empty space
 425    COPY_PARAMS_EQ_REQUIRED = False
 426
 427    # Whether COPY statement has INTO keyword
 428    COPY_HAS_INTO_KEYWORD = True
 429
 430    # Whether the conditional TRY(expression) function is supported
 431    TRY_SUPPORTED = True
 432
 433    # Whether the UESCAPE syntax in unicode strings is supported
 434    SUPPORTS_UESCAPE = True
 435
 436    # The keyword to use when generating a star projection with excluded columns
 437    STAR_EXCEPT = "EXCEPT"
 438
 439    # The HEX function name
 440    HEX_FUNC = "HEX"
 441
 442    # The keywords to use when prefixing & separating WITH based properties
 443    WITH_PROPERTIES_PREFIX = "WITH"
 444
 445    # Whether to quote the generated expression of exp.JsonPath
 446    QUOTE_JSON_PATH = True
 447
 448    # Whether the text pattern/fill (3rd) parameter of RPAD()/LPAD() is optional (defaults to space)
 449    PAD_FILL_PATTERN_IS_REQUIRED = False
 450
 451    # Whether a projection can explode into multiple rows, e.g. by unnesting an array.
 452    SUPPORTS_EXPLODING_PROJECTIONS = True
 453
 454    # Whether ARRAY_CONCAT can be generated with varlen args or if it should be reduced to 2-arg version
 455    ARRAY_CONCAT_IS_VAR_LEN = True
 456
 457    # Whether CONVERT_TIMEZONE() is supported; if not, it will be generated as exp.AtTimeZone
 458    SUPPORTS_CONVERT_TIMEZONE = False
 459
 460    # Whether MEDIAN(expr) is supported; if not, it will be generated as PERCENTILE_CONT(expr, 0.5)
 461    SUPPORTS_MEDIAN = True
 462
 463    # Whether UNIX_SECONDS(timestamp) is supported
 464    SUPPORTS_UNIX_SECONDS = False
 465
 466    # Whether to wrap <props> in `AlterSet`, e.g., ALTER ... SET (<props>)
 467    ALTER_SET_WRAPPED = False
 468
 469    # Whether to normalize the date parts in EXTRACT(<date_part> FROM <expr>) into a common representation
 470    # For instance, to extract the day of week in ISO semantics, one can use ISODOW, DAYOFWEEKISO etc depending on the dialect.
 471    # TODO: The normalization should be done by default once we've tested it across all dialects.
 472    NORMALIZE_EXTRACT_DATE_PARTS = False
 473
 474    # The name to generate for the JSONPath expression. If `None`, only `this` will be generated
 475    PARSE_JSON_NAME: t.Optional[str] = "PARSE_JSON"
 476
 477    # The function name of the exp.ArraySize expression
 478    ARRAY_SIZE_NAME: str = "ARRAY_LENGTH"
 479
 480    # The syntax to use when altering the type of a column
 481    ALTER_SET_TYPE = "SET DATA TYPE"
 482
 483    # Whether exp.ArraySize should generate the dimension arg too (valid for Postgres & DuckDB)
 484    # None -> Doesn't support it at all
 485    # False (DuckDB) -> Has backwards-compatible support, but preferably generated without
 486    # True (Postgres) -> Explicitly requires it
 487    ARRAY_SIZE_DIM_REQUIRED: t.Optional[bool] = None
 488
 489    # Whether a multi-argument DECODE(...) function is supported. If not, a CASE expression is generated
 490    SUPPORTS_DECODE_CASE = True
 491
 492    # Whether SYMMETRIC and ASYMMETRIC flags are supported with BETWEEN expression
 493    SUPPORTS_BETWEEN_FLAGS = False
 494
 495    # Whether LIKE and ILIKE support quantifiers such as LIKE ANY/ALL/SOME
 496    SUPPORTS_LIKE_QUANTIFIERS = True
 497
 498    TYPE_MAPPING = {
 499        exp.DataType.Type.DATETIME2: "TIMESTAMP",
 500        exp.DataType.Type.NCHAR: "CHAR",
 501        exp.DataType.Type.NVARCHAR: "VARCHAR",
 502        exp.DataType.Type.MEDIUMTEXT: "TEXT",
 503        exp.DataType.Type.LONGTEXT: "TEXT",
 504        exp.DataType.Type.TINYTEXT: "TEXT",
 505        exp.DataType.Type.BLOB: "VARBINARY",
 506        exp.DataType.Type.MEDIUMBLOB: "BLOB",
 507        exp.DataType.Type.LONGBLOB: "BLOB",
 508        exp.DataType.Type.TINYBLOB: "BLOB",
 509        exp.DataType.Type.INET: "INET",
 510        exp.DataType.Type.ROWVERSION: "VARBINARY",
 511        exp.DataType.Type.SMALLDATETIME: "TIMESTAMP",
 512    }
 513
 514    TIME_PART_SINGULARS = {
 515        "MICROSECONDS": "MICROSECOND",
 516        "SECONDS": "SECOND",
 517        "MINUTES": "MINUTE",
 518        "HOURS": "HOUR",
 519        "DAYS": "DAY",
 520        "WEEKS": "WEEK",
 521        "MONTHS": "MONTH",
 522        "QUARTERS": "QUARTER",
 523        "YEARS": "YEAR",
 524    }
 525
 526    AFTER_HAVING_MODIFIER_TRANSFORMS = {
 527        "cluster": lambda self, e: self.sql(e, "cluster"),
 528        "distribute": lambda self, e: self.sql(e, "distribute"),
 529        "sort": lambda self, e: self.sql(e, "sort"),
 530        "windows": lambda self, e: (
 531            self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True)
 532            if e.args.get("windows")
 533            else ""
 534        ),
 535        "qualify": lambda self, e: self.sql(e, "qualify"),
 536    }
 537
 538    TOKEN_MAPPING: t.Dict[TokenType, str] = {}
 539
 540    STRUCT_DELIMITER = ("<", ">")
 541
 542    PARAMETER_TOKEN = "@"
 543    NAMED_PLACEHOLDER_TOKEN = ":"
 544
 545    EXPRESSION_PRECEDES_PROPERTIES_CREATABLES: t.Set[str] = set()
 546
 547    PROPERTIES_LOCATION = {
 548        exp.AllowedValuesProperty: exp.Properties.Location.POST_SCHEMA,
 549        exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
 550        exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
 551        exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA,
 552        exp.BackupProperty: exp.Properties.Location.POST_SCHEMA,
 553        exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
 554        exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
 555        exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
 556        exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
 557        exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA,
 558        exp.Cluster: exp.Properties.Location.POST_SCHEMA,
 559        exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA,
 560        exp.DistributedByProperty: exp.Properties.Location.POST_SCHEMA,
 561        exp.DuplicateKeyProperty: exp.Properties.Location.POST_SCHEMA,
 562        exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
 563        exp.DataDeletionProperty: exp.Properties.Location.POST_SCHEMA,
 564        exp.DefinerProperty: exp.Properties.Location.POST_CREATE,
 565        exp.DictRange: exp.Properties.Location.POST_SCHEMA,
 566        exp.DictProperty: exp.Properties.Location.POST_SCHEMA,
 567        exp.DynamicProperty: exp.Properties.Location.POST_CREATE,
 568        exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA,
 569        exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA,
 570        exp.EmptyProperty: exp.Properties.Location.POST_SCHEMA,
 571        exp.EncodeProperty: exp.Properties.Location.POST_EXPRESSION,
 572        exp.EngineProperty: exp.Properties.Location.POST_SCHEMA,
 573        exp.EnviromentProperty: exp.Properties.Location.POST_SCHEMA,
 574        exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA,
 575        exp.ExternalProperty: exp.Properties.Location.POST_CREATE,
 576        exp.FallbackProperty: exp.Properties.Location.POST_NAME,
 577        exp.FileFormatProperty: exp.Properties.Location.POST_WITH,
 578        exp.FreespaceProperty: exp.Properties.Location.POST_NAME,
 579        exp.GlobalProperty: exp.Properties.Location.POST_CREATE,
 580        exp.HeapProperty: exp.Properties.Location.POST_WITH,
 581        exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA,
 582        exp.IcebergProperty: exp.Properties.Location.POST_CREATE,
 583        exp.IncludeProperty: exp.Properties.Location.POST_SCHEMA,
 584        exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA,
 585        exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME,
 586        exp.JournalProperty: exp.Properties.Location.POST_NAME,
 587        exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA,
 588        exp.LikeProperty: exp.Properties.Location.POST_SCHEMA,
 589        exp.LocationProperty: exp.Properties.Location.POST_SCHEMA,
 590        exp.LockProperty: exp.Properties.Location.POST_SCHEMA,
 591        exp.LockingProperty: exp.Properties.Location.POST_ALIAS,
 592        exp.LogProperty: exp.Properties.Location.POST_NAME,
 593        exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
 594        exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME,
 595        exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION,
 596        exp.OnProperty: exp.Properties.Location.POST_SCHEMA,
 597        exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION,
 598        exp.Order: exp.Properties.Location.POST_SCHEMA,
 599        exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA,
 600        exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
 601        exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA,
 602        exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA,
 603        exp.Property: exp.Properties.Location.POST_WITH,
 604        exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA,
 605        exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA,
 606        exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA,
 607        exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA,
 608        exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA,
 609        exp.SampleProperty: exp.Properties.Location.POST_SCHEMA,
 610        exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
 611        exp.SecureProperty: exp.Properties.Location.POST_CREATE,
 612        exp.SecurityProperty: exp.Properties.Location.POST_SCHEMA,
 613        exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
 614        exp.Set: exp.Properties.Location.POST_SCHEMA,
 615        exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA,
 616        exp.SetProperty: exp.Properties.Location.POST_CREATE,
 617        exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA,
 618        exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION,
 619        exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION,
 620        exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
 621        exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA,
 622        exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
 623        exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
 624        exp.StorageHandlerProperty: exp.Properties.Location.POST_SCHEMA,
 625        exp.StreamingTableProperty: exp.Properties.Location.POST_CREATE,
 626        exp.StrictProperty: exp.Properties.Location.POST_SCHEMA,
 627        exp.Tags: exp.Properties.Location.POST_WITH,
 628        exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
 629        exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA,
 630        exp.TransientProperty: exp.Properties.Location.POST_CREATE,
 631        exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA,
 632        exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA,
 633        exp.UnloggedProperty: exp.Properties.Location.POST_CREATE,
 634        exp.UsingTemplateProperty: exp.Properties.Location.POST_SCHEMA,
 635        exp.ViewAttributeProperty: exp.Properties.Location.POST_SCHEMA,
 636        exp.VolatileProperty: exp.Properties.Location.POST_CREATE,
 637        exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION,
 638        exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME,
 639        exp.WithProcedureOptions: exp.Properties.Location.POST_SCHEMA,
 640        exp.WithSchemaBindingProperty: exp.Properties.Location.POST_SCHEMA,
 641        exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA,
 642        exp.ForceProperty: exp.Properties.Location.POST_CREATE,
 643    }
 644
 645    # Keywords that can't be used as unquoted identifier names
 646    RESERVED_KEYWORDS: t.Set[str] = set()
 647
 648    # Expressions whose comments are separated from them for better formatting
 649    WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
 650        exp.Command,
 651        exp.Create,
 652        exp.Describe,
 653        exp.Delete,
 654        exp.Drop,
 655        exp.From,
 656        exp.Insert,
 657        exp.Join,
 658        exp.MultitableInserts,
 659        exp.Order,
 660        exp.Group,
 661        exp.Having,
 662        exp.Select,
 663        exp.SetOperation,
 664        exp.Update,
 665        exp.Where,
 666        exp.With,
 667    )
 668
 669    # Expressions that should not have their comments generated in maybe_comment
 670    EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
 671        exp.Binary,
 672        exp.SetOperation,
 673    )
 674
 675    # Expressions that can remain unwrapped when appearing in the context of an INTERVAL
 676    UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = (
 677        exp.Column,
 678        exp.Literal,
 679        exp.Neg,
 680        exp.Paren,
 681    )
 682
 683    PARAMETERIZABLE_TEXT_TYPES = {
 684        exp.DataType.Type.NVARCHAR,
 685        exp.DataType.Type.VARCHAR,
 686        exp.DataType.Type.CHAR,
 687        exp.DataType.Type.NCHAR,
 688    }
 689
 690    # Expressions that need to have all CTEs under them bubbled up to them
 691    EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set()
 692
 693    RESPECT_IGNORE_NULLS_UNSUPPORTED_EXPRESSIONS: t.Tuple[t.Type[exp.Expression], ...] = ()
 694
 695    SAFE_JSON_PATH_KEY_RE = exp.SAFE_IDENTIFIER_RE
 696
 697    SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
 698
 699    __slots__ = (
 700        "pretty",
 701        "identify",
 702        "normalize",
 703        "pad",
 704        "_indent",
 705        "normalize_functions",
 706        "unsupported_level",
 707        "max_unsupported",
 708        "leading_comma",
 709        "max_text_width",
 710        "comments",
 711        "dialect",
 712        "unsupported_messages",
 713        "_escaped_quote_end",
 714        "_escaped_identifier_end",
 715        "_next_name",
 716        "_identifier_start",
 717        "_identifier_end",
 718        "_quote_json_path_key_using_brackets",
 719    )
 720
 721    def __init__(
 722        self,
 723        pretty: t.Optional[bool] = None,
 724        identify: str | bool = False,
 725        normalize: bool = False,
 726        pad: int = 2,
 727        indent: int = 2,
 728        normalize_functions: t.Optional[str | bool] = None,
 729        unsupported_level: ErrorLevel = ErrorLevel.WARN,
 730        max_unsupported: int = 3,
 731        leading_comma: bool = False,
 732        max_text_width: int = 80,
 733        comments: bool = True,
 734        dialect: DialectType = None,
 735    ):
 736        import sqlglot
 737        from sqlglot.dialects import Dialect
 738
 739        self.pretty = pretty if pretty is not None else sqlglot.pretty
 740        self.identify = identify
 741        self.normalize = normalize
 742        self.pad = pad
 743        self._indent = indent
 744        self.unsupported_level = unsupported_level
 745        self.max_unsupported = max_unsupported
 746        self.leading_comma = leading_comma
 747        self.max_text_width = max_text_width
 748        self.comments = comments
 749        self.dialect = Dialect.get_or_raise(dialect)
 750
 751        # This is both a Dialect property and a Generator argument, so we prioritize the latter
 752        self.normalize_functions = (
 753            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
 754        )
 755
 756        self.unsupported_messages: t.List[str] = []
 757        self._escaped_quote_end: str = (
 758            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
 759        )
 760        self._escaped_identifier_end = self.dialect.IDENTIFIER_END * 2
 761
 762        self._next_name = name_sequence("_t")
 763
 764        self._identifier_start = self.dialect.IDENTIFIER_START
 765        self._identifier_end = self.dialect.IDENTIFIER_END
 766
 767        self._quote_json_path_key_using_brackets = True
 768
 769    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
 770        """
 771        Generates the SQL string corresponding to the given syntax tree.
 772
 773        Args:
 774            expression: The syntax tree.
 775            copy: Whether to copy the expression. The generator performs mutations so
 776                it is safer to copy.
 777
 778        Returns:
 779            The SQL string corresponding to `expression`.
 780        """
 781        if copy:
 782            expression = expression.copy()
 783
 784        expression = self.preprocess(expression)
 785
 786        self.unsupported_messages = []
 787        sql = self.sql(expression).strip()
 788
 789        if self.pretty:
 790            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
 791
 792        if self.unsupported_level == ErrorLevel.IGNORE:
 793            return sql
 794
 795        if self.unsupported_level == ErrorLevel.WARN:
 796            for msg in self.unsupported_messages:
 797                logger.warning(msg)
 798        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
 799            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
 800
 801        return sql
 802
 803    def preprocess(self, expression: exp.Expression) -> exp.Expression:
 804        """Apply generic preprocessing transformations to a given expression."""
 805        expression = self._move_ctes_to_top_level(expression)
 806
 807        if self.ENSURE_BOOLS:
 808            from sqlglot.transforms import ensure_bools
 809
 810            expression = ensure_bools(expression)
 811
 812        return expression
 813
 814    def _move_ctes_to_top_level(self, expression: E) -> E:
 815        if (
 816            not expression.parent
 817            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
 818            and any(node.parent is not expression for node in expression.find_all(exp.With))
 819        ):
 820            from sqlglot.transforms import move_ctes_to_top_level
 821
 822            expression = move_ctes_to_top_level(expression)
 823        return expression
 824
 825    def unsupported(self, message: str) -> None:
 826        if self.unsupported_level == ErrorLevel.IMMEDIATE:
 827            raise UnsupportedError(message)
 828        self.unsupported_messages.append(message)
 829
 830    def sep(self, sep: str = " ") -> str:
 831        return f"{sep.strip()}\n" if self.pretty else sep
 832
 833    def seg(self, sql: str, sep: str = " ") -> str:
 834        return f"{self.sep(sep)}{sql}"
 835
 836    def sanitize_comment(self, comment: str) -> str:
 837        comment = " " + comment if comment[0].strip() else comment
 838        comment = comment + " " if comment[-1].strip() else comment
 839
 840        if not self.dialect.tokenizer_class.NESTED_COMMENTS:
 841            # Necessary workaround to avoid syntax errors due to nesting: /* ... */ ... */
 842            comment = comment.replace("*/", "* /")
 843
 844        return comment
 845
 846    def maybe_comment(
 847        self,
 848        sql: str,
 849        expression: t.Optional[exp.Expression] = None,
 850        comments: t.Optional[t.List[str]] = None,
 851        separated: bool = False,
 852    ) -> str:
 853        comments = (
 854            ((expression and expression.comments) if comments is None else comments)  # type: ignore
 855            if self.comments
 856            else None
 857        )
 858
 859        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
 860            return sql
 861
 862        comments_sql = " ".join(
 863            f"/*{self.sanitize_comment(comment)}*/" for comment in comments if comment
 864        )
 865
 866        if not comments_sql:
 867            return sql
 868
 869        comments_sql = self._replace_line_breaks(comments_sql)
 870
 871        if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS):
 872            return (
 873                f"{self.sep()}{comments_sql}{sql}"
 874                if not sql or sql[0].isspace()
 875                else f"{comments_sql}{self.sep()}{sql}"
 876            )
 877
 878        return f"{sql} {comments_sql}"
 879
 880    def wrap(self, expression: exp.Expression | str) -> str:
 881        this_sql = (
 882            self.sql(expression)
 883            if isinstance(expression, exp.UNWRAPPED_QUERIES)
 884            else self.sql(expression, "this")
 885        )
 886        if not this_sql:
 887            return "()"
 888
 889        this_sql = self.indent(this_sql, level=1, pad=0)
 890        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
 891
 892    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
 893        original = self.identify
 894        self.identify = False
 895        result = func(*args, **kwargs)
 896        self.identify = original
 897        return result
 898
 899    def normalize_func(self, name: str) -> str:
 900        if self.normalize_functions == "upper" or self.normalize_functions is True:
 901            return name.upper()
 902        if self.normalize_functions == "lower":
 903            return name.lower()
 904        return name
 905
 906    def indent(
 907        self,
 908        sql: str,
 909        level: int = 0,
 910        pad: t.Optional[int] = None,
 911        skip_first: bool = False,
 912        skip_last: bool = False,
 913    ) -> str:
 914        if not self.pretty or not sql:
 915            return sql
 916
 917        pad = self.pad if pad is None else pad
 918        lines = sql.split("\n")
 919
 920        return "\n".join(
 921            (
 922                line
 923                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
 924                else f"{' ' * (level * self._indent + pad)}{line}"
 925            )
 926            for i, line in enumerate(lines)
 927        )
 928
 929    def sql(
 930        self,
 931        expression: t.Optional[str | exp.Expression],
 932        key: t.Optional[str] = None,
 933        comment: bool = True,
 934    ) -> str:
 935        if not expression:
 936            return ""
 937
 938        if isinstance(expression, str):
 939            return expression
 940
 941        if key:
 942            value = expression.args.get(key)
 943            if value:
 944                return self.sql(value)
 945            return ""
 946
 947        transform = self.TRANSFORMS.get(expression.__class__)
 948
 949        if callable(transform):
 950            sql = transform(self, expression)
 951        elif isinstance(expression, exp.Expression):
 952            exp_handler_name = f"{expression.key}_sql"
 953
 954            if hasattr(self, exp_handler_name):
 955                sql = getattr(self, exp_handler_name)(expression)
 956            elif isinstance(expression, exp.Func):
 957                sql = self.function_fallback_sql(expression)
 958            elif isinstance(expression, exp.Property):
 959                sql = self.property_sql(expression)
 960            else:
 961                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
 962        else:
 963            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
 964
 965        return self.maybe_comment(sql, expression) if self.comments and comment else sql
 966
 967    def uncache_sql(self, expression: exp.Uncache) -> str:
 968        table = self.sql(expression, "this")
 969        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
 970        return f"UNCACHE TABLE{exists_sql} {table}"
 971
 972    def cache_sql(self, expression: exp.Cache) -> str:
 973        lazy = " LAZY" if expression.args.get("lazy") else ""
 974        table = self.sql(expression, "this")
 975        options = expression.args.get("options")
 976        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
 977        sql = self.sql(expression, "expression")
 978        sql = f" AS{self.sep()}{sql}" if sql else ""
 979        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
 980        return self.prepend_ctes(expression, sql)
 981
 982    def characterset_sql(self, expression: exp.CharacterSet) -> str:
 983        if isinstance(expression.parent, exp.Cast):
 984            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
 985        default = "DEFAULT " if expression.args.get("default") else ""
 986        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
 987
 988    def column_parts(self, expression: exp.Column) -> str:
 989        return ".".join(
 990            self.sql(part)
 991            for part in (
 992                expression.args.get("catalog"),
 993                expression.args.get("db"),
 994                expression.args.get("table"),
 995                expression.args.get("this"),
 996            )
 997            if part
 998        )
 999
1000    def column_sql(self, expression: exp.Column) -> str:
1001        join_mark = " (+)" if expression.args.get("join_mark") else ""
1002
1003        if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS:
1004            join_mark = ""
1005            self.unsupported("Outer join syntax using the (+) operator is not supported.")
1006
1007        return f"{self.column_parts(expression)}{join_mark}"
1008
1009    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
1010        this = self.sql(expression, "this")
1011        this = f" {this}" if this else ""
1012        position = self.sql(expression, "position")
1013        return f"{position}{this}"
1014
1015    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
1016        column = self.sql(expression, "this")
1017        kind = self.sql(expression, "kind")
1018        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
1019        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
1020        kind = f"{sep}{kind}" if kind else ""
1021        constraints = f" {constraints}" if constraints else ""
1022        position = self.sql(expression, "position")
1023        position = f" {position}" if position else ""
1024
1025        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
1026            kind = ""
1027
1028        return f"{exists}{column}{kind}{constraints}{position}"
1029
1030    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
1031        this = self.sql(expression, "this")
1032        kind_sql = self.sql(expression, "kind").strip()
1033        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
1034
1035    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
1036        this = self.sql(expression, "this")
1037        if expression.args.get("not_null"):
1038            persisted = " PERSISTED NOT NULL"
1039        elif expression.args.get("persisted"):
1040            persisted = " PERSISTED"
1041        else:
1042            persisted = ""
1043
1044        return f"AS {this}{persisted}"
1045
1046    def autoincrementcolumnconstraint_sql(self, _) -> str:
1047        return self.token_sql(TokenType.AUTO_INCREMENT)
1048
1049    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
1050        if isinstance(expression.this, list):
1051            this = self.wrap(self.expressions(expression, key="this", flat=True))
1052        else:
1053            this = self.sql(expression, "this")
1054
1055        return f"COMPRESS {this}"
1056
1057    def generatedasidentitycolumnconstraint_sql(
1058        self, expression: exp.GeneratedAsIdentityColumnConstraint
1059    ) -> str:
1060        this = ""
1061        if expression.this is not None:
1062            on_null = " ON NULL" if expression.args.get("on_null") else ""
1063            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
1064
1065        start = expression.args.get("start")
1066        start = f"START WITH {start}" if start else ""
1067        increment = expression.args.get("increment")
1068        increment = f" INCREMENT BY {increment}" if increment else ""
1069        minvalue = expression.args.get("minvalue")
1070        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
1071        maxvalue = expression.args.get("maxvalue")
1072        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
1073        cycle = expression.args.get("cycle")
1074        cycle_sql = ""
1075
1076        if cycle is not None:
1077            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
1078            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
1079
1080        sequence_opts = ""
1081        if start or increment or cycle_sql:
1082            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
1083            sequence_opts = f" ({sequence_opts.strip()})"
1084
1085        expr = self.sql(expression, "expression")
1086        expr = f"({expr})" if expr else "IDENTITY"
1087
1088        return f"GENERATED{this} AS {expr}{sequence_opts}"
1089
1090    def generatedasrowcolumnconstraint_sql(
1091        self, expression: exp.GeneratedAsRowColumnConstraint
1092    ) -> str:
1093        start = "START" if expression.args.get("start") else "END"
1094        hidden = " HIDDEN" if expression.args.get("hidden") else ""
1095        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
1096
1097    def periodforsystemtimeconstraint_sql(
1098        self, expression: exp.PeriodForSystemTimeConstraint
1099    ) -> str:
1100        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
1101
1102    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
1103        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
1104
1105    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
1106        desc = expression.args.get("desc")
1107        if desc is not None:
1108            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
1109        options = self.expressions(expression, key="options", flat=True, sep=" ")
1110        options = f" {options}" if options else ""
1111        return f"PRIMARY KEY{options}"
1112
1113    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
1114        this = self.sql(expression, "this")
1115        this = f" {this}" if this else ""
1116        index_type = expression.args.get("index_type")
1117        index_type = f" USING {index_type}" if index_type else ""
1118        on_conflict = self.sql(expression, "on_conflict")
1119        on_conflict = f" {on_conflict}" if on_conflict else ""
1120        nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else ""
1121        options = self.expressions(expression, key="options", flat=True, sep=" ")
1122        options = f" {options}" if options else ""
1123        return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}{options}"
1124
1125    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
1126        return self.sql(expression, "this")
1127
1128    def create_sql(self, expression: exp.Create) -> str:
1129        kind = self.sql(expression, "kind")
1130        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
1131        properties = expression.args.get("properties")
1132        properties_locs = self.locate_properties(properties) if properties else defaultdict()
1133
1134        this = self.createable_sql(expression, properties_locs)
1135
1136        properties_sql = ""
1137        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
1138            exp.Properties.Location.POST_WITH
1139        ):
1140            props_ast = exp.Properties(
1141                expressions=[
1142                    *properties_locs[exp.Properties.Location.POST_SCHEMA],
1143                    *properties_locs[exp.Properties.Location.POST_WITH],
1144                ]
1145            )
1146            props_ast.parent = expression
1147            properties_sql = self.sql(props_ast)
1148
1149            if properties_locs.get(exp.Properties.Location.POST_SCHEMA):
1150                properties_sql = self.sep() + properties_sql
1151            elif not self.pretty:
1152                # Standalone POST_WITH properties need a leading whitespace in non-pretty mode
1153                properties_sql = f" {properties_sql}"
1154
1155        begin = " BEGIN" if expression.args.get("begin") else ""
1156        end = " END" if expression.args.get("end") else ""
1157
1158        expression_sql = self.sql(expression, "expression")
1159        if expression_sql:
1160            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
1161
1162            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
1163                postalias_props_sql = ""
1164                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
1165                    postalias_props_sql = self.properties(
1166                        exp.Properties(
1167                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
1168                        ),
1169                        wrapped=False,
1170                    )
1171                postalias_props_sql = f" {postalias_props_sql}" if postalias_props_sql else ""
1172                expression_sql = f" AS{postalias_props_sql}{expression_sql}"
1173
1174        postindex_props_sql = ""
1175        if properties_locs.get(exp.Properties.Location.POST_INDEX):
1176            postindex_props_sql = self.properties(
1177                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
1178                wrapped=False,
1179                prefix=" ",
1180            )
1181
1182        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
1183        indexes = f" {indexes}" if indexes else ""
1184        index_sql = indexes + postindex_props_sql
1185
1186        replace = " OR REPLACE" if expression.args.get("replace") else ""
1187        refresh = " OR REFRESH" if expression.args.get("refresh") else ""
1188        unique = " UNIQUE" if expression.args.get("unique") else ""
1189
1190        clustered = expression.args.get("clustered")
1191        if clustered is None:
1192            clustered_sql = ""
1193        elif clustered:
1194            clustered_sql = " CLUSTERED COLUMNSTORE"
1195        else:
1196            clustered_sql = " NONCLUSTERED COLUMNSTORE"
1197
1198        postcreate_props_sql = ""
1199        if properties_locs.get(exp.Properties.Location.POST_CREATE):
1200            postcreate_props_sql = self.properties(
1201                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
1202                sep=" ",
1203                prefix=" ",
1204                wrapped=False,
1205            )
1206
1207        modifiers = "".join((clustered_sql, replace, refresh, unique, postcreate_props_sql))
1208
1209        postexpression_props_sql = ""
1210        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
1211            postexpression_props_sql = self.properties(
1212                exp.Properties(
1213                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
1214                ),
1215                sep=" ",
1216                prefix=" ",
1217                wrapped=False,
1218            )
1219
1220        concurrently = " CONCURRENTLY" if expression.args.get("concurrently") else ""
1221        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
1222        no_schema_binding = (
1223            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
1224        )
1225
1226        clone = self.sql(expression, "clone")
1227        clone = f" {clone}" if clone else ""
1228
1229        if kind in self.EXPRESSION_PRECEDES_PROPERTIES_CREATABLES:
1230            properties_expression = f"{expression_sql}{properties_sql}"
1231        else:
1232            properties_expression = f"{properties_sql}{expression_sql}"
1233
1234        expression_sql = f"CREATE{modifiers} {kind}{concurrently}{exists_sql} {this}{properties_expression}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
1235        return self.prepend_ctes(expression, expression_sql)
1236
1237    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
1238        start = self.sql(expression, "start")
1239        start = f"START WITH {start}" if start else ""
1240        increment = self.sql(expression, "increment")
1241        increment = f" INCREMENT BY {increment}" if increment else ""
1242        minvalue = self.sql(expression, "minvalue")
1243        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
1244        maxvalue = self.sql(expression, "maxvalue")
1245        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
1246        owned = self.sql(expression, "owned")
1247        owned = f" OWNED BY {owned}" if owned else ""
1248
1249        cache = expression.args.get("cache")
1250        if cache is None:
1251            cache_str = ""
1252        elif cache is True:
1253            cache_str = " CACHE"
1254        else:
1255            cache_str = f" CACHE {cache}"
1256
1257        options = self.expressions(expression, key="options", flat=True, sep=" ")
1258        options = f" {options}" if options else ""
1259
1260        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()
1261
1262    def clone_sql(self, expression: exp.Clone) -> str:
1263        this = self.sql(expression, "this")
1264        shallow = "SHALLOW " if expression.args.get("shallow") else ""
1265        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
1266        return f"{shallow}{keyword} {this}"
1267
1268    def describe_sql(self, expression: exp.Describe) -> str:
1269        style = expression.args.get("style")
1270        style = f" {style}" if style else ""
1271        partition = self.sql(expression, "partition")
1272        partition = f" {partition}" if partition else ""
1273        format = self.sql(expression, "format")
1274        format = f" {format}" if format else ""
1275
1276        return f"DESCRIBE{style}{format} {self.sql(expression, 'this')}{partition}"
1277
1278    def heredoc_sql(self, expression: exp.Heredoc) -> str:
1279        tag = self.sql(expression, "tag")
1280        return f"${tag}${self.sql(expression, 'this')}${tag}$"
1281
1282    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
1283        with_ = self.sql(expression, "with")
1284        if with_:
1285            sql = f"{with_}{self.sep()}{sql}"
1286        return sql
1287
1288    def with_sql(self, expression: exp.With) -> str:
1289        sql = self.expressions(expression, flat=True)
1290        recursive = (
1291            "RECURSIVE "
1292            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
1293            else ""
1294        )
1295        search = self.sql(expression, "search")
1296        search = f" {search}" if search else ""
1297
1298        return f"WITH {recursive}{sql}{search}"
1299
1300    def cte_sql(self, expression: exp.CTE) -> str:
1301        alias = expression.args.get("alias")
1302        if alias:
1303            alias.add_comments(expression.pop_comments())
1304
1305        alias_sql = self.sql(expression, "alias")
1306
1307        materialized = expression.args.get("materialized")
1308        if materialized is False:
1309            materialized = "NOT MATERIALIZED "
1310        elif materialized:
1311            materialized = "MATERIALIZED "
1312
1313        return f"{alias_sql} AS {materialized or ''}{self.wrap(expression)}"
1314
1315    def tablealias_sql(self, expression: exp.TableAlias) -> str:
1316        alias = self.sql(expression, "this")
1317        columns = self.expressions(expression, key="columns", flat=True)
1318        columns = f"({columns})" if columns else ""
1319
1320        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
1321            columns = ""
1322            self.unsupported("Named columns are not supported in table alias.")
1323
1324        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
1325            alias = self._next_name()
1326
1327        return f"{alias}{columns}"
1328
1329    def bitstring_sql(self, expression: exp.BitString) -> str:
1330        this = self.sql(expression, "this")
1331        if self.dialect.BIT_START:
1332            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
1333        return f"{int(this, 2)}"
1334
1335    def hexstring_sql(
1336        self, expression: exp.HexString, binary_function_repr: t.Optional[str] = None
1337    ) -> str:
1338        this = self.sql(expression, "this")
1339        is_integer_type = expression.args.get("is_integer")
1340
1341        if (is_integer_type and not self.dialect.HEX_STRING_IS_INTEGER_TYPE) or (
1342            not self.dialect.HEX_START and not binary_function_repr
1343        ):
1344            # Integer representation will be returned if:
1345            # - The read dialect treats the hex value as integer literal but not the write
1346            # - The transpilation is not supported (write dialect hasn't set HEX_START or the param flag)
1347            return f"{int(this, 16)}"
1348
1349        if not is_integer_type:
1350            # Read dialect treats the hex value as BINARY/BLOB
1351            if binary_function_repr:
1352                # The write dialect supports the transpilation to its equivalent BINARY/BLOB
1353                return self.func(binary_function_repr, exp.Literal.string(this))
1354            if self.dialect.HEX_STRING_IS_INTEGER_TYPE:
1355                # The write dialect does not support the transpilation, it'll treat the hex value as INTEGER
1356                self.unsupported("Unsupported transpilation from BINARY/BLOB hex string")
1357
1358        return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
1359
1360    def bytestring_sql(self, expression: exp.ByteString) -> str:
1361        this = self.sql(expression, "this")
1362        if self.dialect.BYTE_START:
1363            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
1364        return this
1365
1366    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
1367        this = self.sql(expression, "this")
1368        escape = expression.args.get("escape")
1369
1370        if self.dialect.UNICODE_START:
1371            escape_substitute = r"\\\1"
1372            left_quote, right_quote = self.dialect.UNICODE_START, self.dialect.UNICODE_END
1373        else:
1374            escape_substitute = r"\\u\1"
1375            left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END
1376
1377        if escape:
1378            escape_pattern = re.compile(rf"{escape.name}(\d+)")
1379            escape_sql = f" UESCAPE {self.sql(escape)}" if self.SUPPORTS_UESCAPE else ""
1380        else:
1381            escape_pattern = ESCAPED_UNICODE_RE
1382            escape_sql = ""
1383
1384        if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE):
1385            this = escape_pattern.sub(escape_substitute, this)
1386
1387        return f"{left_quote}{this}{right_quote}{escape_sql}"
1388
1389    def rawstring_sql(self, expression: exp.RawString) -> str:
1390        string = expression.this
1391        if "\\" in self.dialect.tokenizer_class.STRING_ESCAPES:
1392            string = string.replace("\\", "\\\\")
1393
1394        string = self.escape_str(string, escape_backslash=False)
1395        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
1396
1397    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
1398        this = self.sql(expression, "this")
1399        specifier = self.sql(expression, "expression")
1400        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
1401        return f"{this}{specifier}"
1402
1403    def datatype_sql(self, expression: exp.DataType) -> str:
1404        nested = ""
1405        values = ""
1406        interior = self.expressions(expression, flat=True)
1407
1408        type_value = expression.this
1409        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
1410            type_sql = self.sql(expression, "kind")
1411        else:
1412            type_sql = (
1413                self.TYPE_MAPPING.get(type_value, type_value.value)
1414                if isinstance(type_value, exp.DataType.Type)
1415                else type_value
1416            )
1417
1418        if interior:
1419            if expression.args.get("nested"):
1420                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
1421                if expression.args.get("values") is not None:
1422                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
1423                    values = self.expressions(expression, key="values", flat=True)
1424                    values = f"{delimiters[0]}{values}{delimiters[1]}"
1425            elif type_value == exp.DataType.Type.INTERVAL:
1426                nested = f" {interior}"
1427            else:
1428                nested = f"({interior})"
1429
1430        type_sql = f"{type_sql}{nested}{values}"
1431        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
1432            exp.DataType.Type.TIMETZ,
1433            exp.DataType.Type.TIMESTAMPTZ,
1434        ):
1435            type_sql = f"{type_sql} WITH TIME ZONE"
1436
1437        return type_sql
1438
1439    def directory_sql(self, expression: exp.Directory) -> str:
1440        local = "LOCAL " if expression.args.get("local") else ""
1441        row_format = self.sql(expression, "row_format")
1442        row_format = f" {row_format}" if row_format else ""
1443        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
1444
1445    def delete_sql(self, expression: exp.Delete) -> str:
1446        this = self.sql(expression, "this")
1447        this = f" FROM {this}" if this else ""
1448        using = self.sql(expression, "using")
1449        using = f" USING {using}" if using else ""
1450        cluster = self.sql(expression, "cluster")
1451        cluster = f" {cluster}" if cluster else ""
1452        where = self.sql(expression, "where")
1453        returning = self.sql(expression, "returning")
1454        limit = self.sql(expression, "limit")
1455        tables = self.expressions(expression, key="tables")
1456        tables = f" {tables}" if tables else ""
1457        if self.RETURNING_END:
1458            expression_sql = f"{this}{using}{cluster}{where}{returning}{limit}"
1459        else:
1460            expression_sql = f"{returning}{this}{using}{cluster}{where}{limit}"
1461        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
1462
1463    def drop_sql(self, expression: exp.Drop) -> str:
1464        this = self.sql(expression, "this")
1465        expressions = self.expressions(expression, flat=True)
1466        expressions = f" ({expressions})" if expressions else ""
1467        kind = expression.args["kind"]
1468        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
1469        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
1470        concurrently_sql = " CONCURRENTLY" if expression.args.get("concurrently") else ""
1471        on_cluster = self.sql(expression, "cluster")
1472        on_cluster = f" {on_cluster}" if on_cluster else ""
1473        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1474        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
1475        cascade = " CASCADE" if expression.args.get("cascade") else ""
1476        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
1477        purge = " PURGE" if expression.args.get("purge") else ""
1478        return f"DROP{temporary}{materialized} {kind}{concurrently_sql}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"
1479
1480    def set_operation(self, expression: exp.SetOperation) -> str:
1481        op_type = type(expression)
1482        op_name = op_type.key.upper()
1483
1484        distinct = expression.args.get("distinct")
1485        if (
1486            distinct is False
1487            and op_type in (exp.Except, exp.Intersect)
1488            and not self.EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE
1489        ):
1490            self.unsupported(f"{op_name} ALL is not supported")
1491
1492        default_distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[op_type]
1493
1494        if distinct is None:
1495            distinct = default_distinct
1496            if distinct is None:
1497                self.unsupported(f"{op_name} requires DISTINCT or ALL to be specified")
1498
1499        if distinct is default_distinct:
1500            distinct_or_all = ""
1501        else:
1502            distinct_or_all = " DISTINCT" if distinct else " ALL"
1503
1504        side_kind = " ".join(filter(None, [expression.side, expression.kind]))
1505        side_kind = f"{side_kind} " if side_kind else ""
1506
1507        by_name = " BY NAME" if expression.args.get("by_name") else ""
1508        on = self.expressions(expression, key="on", flat=True)
1509        on = f" ON ({on})" if on else ""
1510
1511        return f"{side_kind}{op_name}{distinct_or_all}{by_name}{on}"
1512
1513    def set_operations(self, expression: exp.SetOperation) -> str:
1514        if not self.SET_OP_MODIFIERS:
1515            limit = expression.args.get("limit")
1516            order = expression.args.get("order")
1517
1518            if limit or order:
1519                select = self._move_ctes_to_top_level(
1520                    exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
1521                )
1522
1523                if limit:
1524                    select = select.limit(limit.pop(), copy=False)
1525                if order:
1526                    select = select.order_by(order.pop(), copy=False)
1527                return self.sql(select)
1528
1529        sqls: t.List[str] = []
1530        stack: t.List[t.Union[str, exp.Expression]] = [expression]
1531
1532        while stack:
1533            node = stack.pop()
1534
1535            if isinstance(node, exp.SetOperation):
1536                stack.append(node.expression)
1537                stack.append(
1538                    self.maybe_comment(
1539                        self.set_operation(node), comments=node.comments, separated=True
1540                    )
1541                )
1542                stack.append(node.this)
1543            else:
1544                sqls.append(self.sql(node))
1545
1546        this = self.sep().join(sqls)
1547        this = self.query_modifiers(expression, this)
1548        return self.prepend_ctes(expression, this)
1549
1550    def fetch_sql(self, expression: exp.Fetch) -> str:
1551        direction = expression.args.get("direction")
1552        direction = f" {direction}" if direction else ""
1553        count = self.sql(expression, "count")
1554        count = f" {count}" if count else ""
1555        limit_options = self.sql(expression, "limit_options")
1556        limit_options = f"{limit_options}" if limit_options else " ROWS ONLY"
1557        return f"{self.seg('FETCH')}{direction}{count}{limit_options}"
1558
1559    def limitoptions_sql(self, expression: exp.LimitOptions) -> str:
1560        percent = " PERCENT" if expression.args.get("percent") else ""
1561        rows = " ROWS" if expression.args.get("rows") else ""
1562        with_ties = " WITH TIES" if expression.args.get("with_ties") else ""
1563        if not with_ties and rows:
1564            with_ties = " ONLY"
1565        return f"{percent}{rows}{with_ties}"
1566
1567    def filter_sql(self, expression: exp.Filter) -> str:
1568        if self.AGGREGATE_FILTER_SUPPORTED:
1569            this = self.sql(expression, "this")
1570            where = self.sql(expression, "expression").strip()
1571            return f"{this} FILTER({where})"
1572
1573        agg = expression.this
1574        agg_arg = agg.this
1575        cond = expression.expression.this
1576        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
1577        return self.sql(agg)
1578
1579    def hint_sql(self, expression: exp.Hint) -> str:
1580        if not self.QUERY_HINTS:
1581            self.unsupported("Hints are not supported")
1582            return ""
1583
1584        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
1585
1586    def indexparameters_sql(self, expression: exp.IndexParameters) -> str:
1587        using = self.sql(expression, "using")
1588        using = f" USING {using}" if using else ""
1589        columns = self.expressions(expression, key="columns", flat=True)
1590        columns = f"({columns})" if columns else ""
1591        partition_by = self.expressions(expression, key="partition_by", flat=True)
1592        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
1593        where = self.sql(expression, "where")
1594        include = self.expressions(expression, key="include", flat=True)
1595        if include:
1596            include = f" INCLUDE ({include})"
1597        with_storage = self.expressions(expression, key="with_storage", flat=True)
1598        with_storage = f" WITH ({with_storage})" if with_storage else ""
1599        tablespace = self.sql(expression, "tablespace")
1600        tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
1601        on = self.sql(expression, "on")
1602        on = f" ON {on}" if on else ""
1603
1604        return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}"
1605
1606    def index_sql(self, expression: exp.Index) -> str:
1607        unique = "UNIQUE " if expression.args.get("unique") else ""
1608        primary = "PRIMARY " if expression.args.get("primary") else ""
1609        amp = "AMP " if expression.args.get("amp") else ""
1610        name = self.sql(expression, "this")
1611        name = f"{name} " if name else ""
1612        table = self.sql(expression, "table")
1613        table = f"{self.INDEX_ON} {table}" if table else ""
1614
1615        index = "INDEX " if not table else ""
1616
1617        params = self.sql(expression, "params")
1618        return f"{unique}{primary}{amp}{index}{name}{table}{params}"
1619
1620    def identifier_sql(self, expression: exp.Identifier) -> str:
1621        text = expression.name
1622        lower = text.lower()
1623        text = lower if self.normalize and not expression.quoted else text
1624        text = text.replace(self._identifier_end, self._escaped_identifier_end)
1625        if (
1626            expression.quoted
1627            or self.dialect.can_identify(text, self.identify)
1628            or lower in self.RESERVED_KEYWORDS
1629            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
1630        ):
1631            text = f"{self._identifier_start}{text}{self._identifier_end}"
1632        return text
1633
1634    def hex_sql(self, expression: exp.Hex) -> str:
1635        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
1636        if self.dialect.HEX_LOWERCASE:
1637            text = self.func("LOWER", text)
1638
1639        return text
1640
1641    def lowerhex_sql(self, expression: exp.LowerHex) -> str:
1642        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
1643        if not self.dialect.HEX_LOWERCASE:
1644            text = self.func("LOWER", text)
1645        return text
1646
1647    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
1648        input_format = self.sql(expression, "input_format")
1649        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
1650        output_format = self.sql(expression, "output_format")
1651        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
1652        return self.sep().join((input_format, output_format))
1653
1654    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
1655        string = self.sql(exp.Literal.string(expression.name))
1656        return f"{prefix}{string}"
1657
1658    def partition_sql(self, expression: exp.Partition) -> str:
1659        partition_keyword = "SUBPARTITION" if expression.args.get("subpartition") else "PARTITION"
1660        return f"{partition_keyword}({self.expressions(expression, flat=True)})"
1661
1662    def properties_sql(self, expression: exp.Properties) -> str:
1663        root_properties = []
1664        with_properties = []
1665
1666        for p in expression.expressions:
1667            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1668            if p_loc == exp.Properties.Location.POST_WITH:
1669                with_properties.append(p)
1670            elif p_loc == exp.Properties.Location.POST_SCHEMA:
1671                root_properties.append(p)
1672
1673        root_props_ast = exp.Properties(expressions=root_properties)
1674        root_props_ast.parent = expression.parent
1675
1676        with_props_ast = exp.Properties(expressions=with_properties)
1677        with_props_ast.parent = expression.parent
1678
1679        root_props = self.root_properties(root_props_ast)
1680        with_props = self.with_properties(with_props_ast)
1681
1682        if root_props and with_props and not self.pretty:
1683            with_props = " " + with_props
1684
1685        return root_props + with_props
1686
1687    def root_properties(self, properties: exp.Properties) -> str:
1688        if properties.expressions:
1689            return self.expressions(properties, indent=False, sep=" ")
1690        return ""
1691
1692    def properties(
1693        self,
1694        properties: exp.Properties,
1695        prefix: str = "",
1696        sep: str = ", ",
1697        suffix: str = "",
1698        wrapped: bool = True,
1699    ) -> str:
1700        if properties.expressions:
1701            expressions = self.expressions(properties, sep=sep, indent=False)
1702            if expressions:
1703                expressions = self.wrap(expressions) if wrapped else expressions
1704                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
1705        return ""
1706
1707    def with_properties(self, properties: exp.Properties) -> str:
1708        return self.properties(properties, prefix=self.seg(self.WITH_PROPERTIES_PREFIX, sep=""))
1709
1710    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
1711        properties_locs = defaultdict(list)
1712        for p in properties.expressions:
1713            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1714            if p_loc != exp.Properties.Location.UNSUPPORTED:
1715                properties_locs[p_loc].append(p)
1716            else:
1717                self.unsupported(f"Unsupported property {p.key}")
1718
1719        return properties_locs
1720
1721    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
1722        if isinstance(expression.this, exp.Dot):
1723            return self.sql(expression, "this")
1724        return f"'{expression.name}'" if string_key else expression.name
1725
1726    def property_sql(self, expression: exp.Property) -> str:
1727        property_cls = expression.__class__
1728        if property_cls == exp.Property:
1729            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
1730
1731        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
1732        if not property_name:
1733            self.unsupported(f"Unsupported property {expression.key}")
1734
1735        return f"{property_name}={self.sql(expression, 'this')}"
1736
1737    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
1738        if self.SUPPORTS_CREATE_TABLE_LIKE:
1739            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
1740            options = f" {options}" if options else ""
1741
1742            like = f"LIKE {self.sql(expression, 'this')}{options}"
1743            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
1744                like = f"({like})"
1745
1746            return like
1747
1748        if expression.expressions:
1749            self.unsupported("Transpilation of LIKE property options is unsupported")
1750
1751        select = exp.select("*").from_(expression.this).limit(0)
1752        return f"AS {self.sql(select)}"
1753
1754    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
1755        no = "NO " if expression.args.get("no") else ""
1756        protection = " PROTECTION" if expression.args.get("protection") else ""
1757        return f"{no}FALLBACK{protection}"
1758
1759    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
1760        no = "NO " if expression.args.get("no") else ""
1761        local = expression.args.get("local")
1762        local = f"{local} " if local else ""
1763        dual = "DUAL " if expression.args.get("dual") else ""
1764        before = "BEFORE " if expression.args.get("before") else ""
1765        after = "AFTER " if expression.args.get("after") else ""
1766        return f"{no}{local}{dual}{before}{after}JOURNAL"
1767
1768    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
1769        freespace = self.sql(expression, "this")
1770        percent = " PERCENT" if expression.args.get("percent") else ""
1771        return f"FREESPACE={freespace}{percent}"
1772
1773    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
1774        if expression.args.get("default"):
1775            property = "DEFAULT"
1776        elif expression.args.get("on"):
1777            property = "ON"
1778        else:
1779            property = "OFF"
1780        return f"CHECKSUM={property}"
1781
1782    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
1783        if expression.args.get("no"):
1784            return "NO MERGEBLOCKRATIO"
1785        if expression.args.get("default"):
1786            return "DEFAULT MERGEBLOCKRATIO"
1787
1788        percent = " PERCENT" if expression.args.get("percent") else ""
1789        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
1790
1791    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
1792        default = expression.args.get("default")
1793        minimum = expression.args.get("minimum")
1794        maximum = expression.args.get("maximum")
1795        if default or minimum or maximum:
1796            if default:
1797                prop = "DEFAULT"
1798            elif minimum:
1799                prop = "MINIMUM"
1800            else:
1801                prop = "MAXIMUM"
1802            return f"{prop} DATABLOCKSIZE"
1803        units = expression.args.get("units")
1804        units = f" {units}" if units else ""
1805        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
1806
1807    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
1808        autotemp = expression.args.get("autotemp")
1809        always = expression.args.get("always")
1810        default = expression.args.get("default")
1811        manual = expression.args.get("manual")
1812        never = expression.args.get("never")
1813
1814        if autotemp is not None:
1815            prop = f"AUTOTEMP({self.expressions(autotemp)})"
1816        elif always:
1817            prop = "ALWAYS"
1818        elif default:
1819            prop = "DEFAULT"
1820        elif manual:
1821            prop = "MANUAL"
1822        elif never:
1823            prop = "NEVER"
1824        return f"BLOCKCOMPRESSION={prop}"
1825
1826    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
1827        no = expression.args.get("no")
1828        no = " NO" if no else ""
1829        concurrent = expression.args.get("concurrent")
1830        concurrent = " CONCURRENT" if concurrent else ""
1831        target = self.sql(expression, "target")
1832        target = f" {target}" if target else ""
1833        return f"WITH{no}{concurrent} ISOLATED LOADING{target}"
1834
1835    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
1836        if isinstance(expression.this, list):
1837            return f"IN ({self.expressions(expression, key='this', flat=True)})"
1838        if expression.this:
1839            modulus = self.sql(expression, "this")
1840            remainder = self.sql(expression, "expression")
1841            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
1842
1843        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
1844        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
1845        return f"FROM ({from_expressions}) TO ({to_expressions})"
1846
1847    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
1848        this = self.sql(expression, "this")
1849
1850        for_values_or_default = expression.expression
1851        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
1852            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
1853        else:
1854            for_values_or_default = " DEFAULT"
1855
1856        return f"PARTITION OF {this}{for_values_or_default}"
1857
1858    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
1859        kind = expression.args.get("kind")
1860        this = f" {self.sql(expression, 'this')}" if expression.this else ""
1861        for_or_in = expression.args.get("for_or_in")
1862        for_or_in = f" {for_or_in}" if for_or_in else ""
1863        lock_type = expression.args.get("lock_type")
1864        override = " OVERRIDE" if expression.args.get("override") else ""
1865        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
1866
1867    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
1868        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
1869        statistics = expression.args.get("statistics")
1870        statistics_sql = ""
1871        if statistics is not None:
1872            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
1873        return f"{data_sql}{statistics_sql}"
1874
1875    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
1876        this = self.sql(expression, "this")
1877        this = f"HISTORY_TABLE={this}" if this else ""
1878        data_consistency: t.Optional[str] = self.sql(expression, "data_consistency")
1879        data_consistency = (
1880            f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None
1881        )
1882        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
1883        retention_period = (
1884            f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None
1885        )
1886
1887        if this:
1888            on_sql = self.func("ON", this, data_consistency, retention_period)
1889        else:
1890            on_sql = "ON" if expression.args.get("on") else "OFF"
1891
1892        sql = f"SYSTEM_VERSIONING={on_sql}"
1893
1894        return f"WITH({sql})" if expression.args.get("with") else sql
1895
1896    def insert_sql(self, expression: exp.Insert) -> str:
1897        hint = self.sql(expression, "hint")
1898        overwrite = expression.args.get("overwrite")
1899
1900        if isinstance(expression.this, exp.Directory):
1901            this = " OVERWRITE" if overwrite else " INTO"
1902        else:
1903            this = self.INSERT_OVERWRITE if overwrite else " INTO"
1904
1905        stored = self.sql(expression, "stored")
1906        stored = f" {stored}" if stored else ""
1907        alternative = expression.args.get("alternative")
1908        alternative = f" OR {alternative}" if alternative else ""
1909        ignore = " IGNORE" if expression.args.get("ignore") else ""
1910        is_function = expression.args.get("is_function")
1911        if is_function:
1912            this = f"{this} FUNCTION"
1913        this = f"{this} {self.sql(expression, 'this')}"
1914
1915        exists = " IF EXISTS" if expression.args.get("exists") else ""
1916        where = self.sql(expression, "where")
1917        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
1918        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
1919        on_conflict = self.sql(expression, "conflict")
1920        on_conflict = f" {on_conflict}" if on_conflict else ""
1921        by_name = " BY NAME" if expression.args.get("by_name") else ""
1922        returning = self.sql(expression, "returning")
1923
1924        if self.RETURNING_END:
1925            expression_sql = f"{expression_sql}{on_conflict}{returning}"
1926        else:
1927            expression_sql = f"{returning}{expression_sql}{on_conflict}"
1928
1929        partition_by = self.sql(expression, "partition")
1930        partition_by = f" {partition_by}" if partition_by else ""
1931        settings = self.sql(expression, "settings")
1932        settings = f" {settings}" if settings else ""
1933
1934        source = self.sql(expression, "source")
1935        source = f"TABLE {source}" if source else ""
1936
1937        sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{partition_by}{settings}{where}{expression_sql}{source}"
1938        return self.prepend_ctes(expression, sql)
1939
1940    def introducer_sql(self, expression: exp.Introducer) -> str:
1941        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
1942
1943    def kill_sql(self, expression: exp.Kill) -> str:
1944        kind = self.sql(expression, "kind")
1945        kind = f" {kind}" if kind else ""
1946        this = self.sql(expression, "this")
1947        this = f" {this}" if this else ""
1948        return f"KILL{kind}{this}"
1949
1950    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
1951        return expression.name
1952
1953    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
1954        return expression.name
1955
1956    def onconflict_sql(self, expression: exp.OnConflict) -> str:
1957        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
1958
1959        constraint = self.sql(expression, "constraint")
1960        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
1961
1962        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
1963        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
1964        action = self.sql(expression, "action")
1965
1966        expressions = self.expressions(expression, flat=True)
1967        if expressions:
1968            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
1969            expressions = f" {set_keyword}{expressions}"
1970
1971        where = self.sql(expression, "where")
1972        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}{where}"
1973
1974    def returning_sql(self, expression: exp.Returning) -> str:
1975        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
1976
1977    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
1978        fields = self.sql(expression, "fields")
1979        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
1980        escaped = self.sql(expression, "escaped")
1981        escaped = f" ESCAPED BY {escaped}" if escaped else ""
1982        items = self.sql(expression, "collection_items")
1983        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
1984        keys = self.sql(expression, "map_keys")
1985        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
1986        lines = self.sql(expression, "lines")
1987        lines = f" LINES TERMINATED BY {lines}" if lines else ""
1988        null = self.sql(expression, "null")
1989        null = f" NULL DEFINED AS {null}" if null else ""
1990        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
1991
1992    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
1993        return f"WITH ({self.expressions(expression, flat=True)})"
1994
1995    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
1996        this = f"{self.sql(expression, 'this')} INDEX"
1997        target = self.sql(expression, "target")
1998        target = f" FOR {target}" if target else ""
1999        return f"{this}{target} ({self.expressions(expression, flat=True)})"
2000
2001    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
2002        this = self.sql(expression, "this")
2003        kind = self.sql(expression, "kind")
2004        expr = self.sql(expression, "expression")
2005        return f"{this} ({kind} => {expr})"
2006
2007    def table_parts(self, expression: exp.Table) -> str:
2008        return ".".join(
2009            self.sql(part)
2010            for part in (
2011                expression.args.get("catalog"),
2012                expression.args.get("db"),
2013                expression.args.get("this"),
2014            )
2015            if part is not None
2016        )
2017
2018    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
2019        table = self.table_parts(expression)
2020        only = "ONLY " if expression.args.get("only") else ""
2021        partition = self.sql(expression, "partition")
2022        partition = f" {partition}" if partition else ""
2023        version = self.sql(expression, "version")
2024        version = f" {version}" if version else ""
2025        alias = self.sql(expression, "alias")
2026        alias = f"{sep}{alias}" if alias else ""
2027
2028        sample = self.sql(expression, "sample")
2029        if self.dialect.ALIAS_POST_TABLESAMPLE:
2030            sample_pre_alias = sample
2031            sample_post_alias = ""
2032        else:
2033            sample_pre_alias = ""
2034            sample_post_alias = sample
2035
2036        hints = self.expressions(expression, key="hints", sep=" ")
2037        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
2038        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2039        joins = self.indent(
2040            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
2041        )
2042        laterals = self.expressions(expression, key="laterals", sep="")
2043
2044        file_format = self.sql(expression, "format")
2045        if file_format:
2046            pattern = self.sql(expression, "pattern")
2047            pattern = f", PATTERN => {pattern}" if pattern else ""
2048            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
2049
2050        ordinality = expression.args.get("ordinality") or ""
2051        if ordinality:
2052            ordinality = f" WITH ORDINALITY{alias}"
2053            alias = ""
2054
2055        when = self.sql(expression, "when")
2056        if when:
2057            table = f"{table} {when}"
2058
2059        changes = self.sql(expression, "changes")
2060        changes = f" {changes}" if changes else ""
2061
2062        rows_from = self.expressions(expression, key="rows_from")
2063        if rows_from:
2064            table = f"ROWS FROM {self.wrap(rows_from)}"
2065
2066        return f"{only}{table}{changes}{partition}{version}{file_format}{sample_pre_alias}{alias}{hints}{pivots}{sample_post_alias}{joins}{laterals}{ordinality}"
2067
2068    def tablefromrows_sql(self, expression: exp.TableFromRows) -> str:
2069        table = self.func("TABLE", expression.this)
2070        alias = self.sql(expression, "alias")
2071        alias = f" AS {alias}" if alias else ""
2072        sample = self.sql(expression, "sample")
2073        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2074        joins = self.indent(
2075            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
2076        )
2077        return f"{table}{alias}{pivots}{sample}{joins}"
2078
2079    def tablesample_sql(
2080        self,
2081        expression: exp.TableSample,
2082        tablesample_keyword: t.Optional[str] = None,
2083    ) -> str:
2084        method = self.sql(expression, "method")
2085        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
2086        numerator = self.sql(expression, "bucket_numerator")
2087        denominator = self.sql(expression, "bucket_denominator")
2088        field = self.sql(expression, "bucket_field")
2089        field = f" ON {field}" if field else ""
2090        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
2091        seed = self.sql(expression, "seed")
2092        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
2093
2094        size = self.sql(expression, "size")
2095        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
2096            size = f"{size} ROWS"
2097
2098        percent = self.sql(expression, "percent")
2099        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
2100            percent = f"{percent} PERCENT"
2101
2102        expr = f"{bucket}{percent}{size}"
2103        if self.TABLESAMPLE_REQUIRES_PARENS:
2104            expr = f"({expr})"
2105
2106        return f" {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}"
2107
2108    def pivot_sql(self, expression: exp.Pivot) -> str:
2109        expressions = self.expressions(expression, flat=True)
2110        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
2111
2112        group = self.sql(expression, "group")
2113
2114        if expression.this:
2115            this = self.sql(expression, "this")
2116            if not expressions:
2117                return f"UNPIVOT {this}"
2118
2119            on = f"{self.seg('ON')} {expressions}"
2120            into = self.sql(expression, "into")
2121            into = f"{self.seg('INTO')} {into}" if into else ""
2122            using = self.expressions(expression, key="using", flat=True)
2123            using = f"{self.seg('USING')} {using}" if using else ""
2124            return f"{direction} {this}{on}{into}{using}{group}"
2125
2126        alias = self.sql(expression, "alias")
2127        alias = f" AS {alias}" if alias else ""
2128
2129        fields = self.expressions(
2130            expression,
2131            "fields",
2132            sep=" ",
2133            dynamic=True,
2134            new_line=True,
2135            skip_first=True,
2136            skip_last=True,
2137        )
2138
2139        include_nulls = expression.args.get("include_nulls")
2140        if include_nulls is not None:
2141            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
2142        else:
2143            nulls = ""
2144
2145        default_on_null = self.sql(expression, "default_on_null")
2146        default_on_null = f" DEFAULT ON NULL ({default_on_null})" if default_on_null else ""
2147        return f"{self.seg(direction)}{nulls}({expressions} FOR {fields}{default_on_null}{group}){alias}"
2148
2149    def version_sql(self, expression: exp.Version) -> str:
2150        this = f"FOR {expression.name}"
2151        kind = expression.text("kind")
2152        expr = self.sql(expression, "expression")
2153        return f"{this} {kind} {expr}"
2154
2155    def tuple_sql(self, expression: exp.Tuple) -> str:
2156        return f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
2157
2158    def update_sql(self, expression: exp.Update) -> str:
2159        this = self.sql(expression, "this")
2160        set_sql = self.expressions(expression, flat=True)
2161        from_sql = self.sql(expression, "from")
2162        where_sql = self.sql(expression, "where")
2163        returning = self.sql(expression, "returning")
2164        order = self.sql(expression, "order")
2165        limit = self.sql(expression, "limit")
2166        if self.RETURNING_END:
2167            expression_sql = f"{from_sql}{where_sql}{returning}"
2168        else:
2169            expression_sql = f"{returning}{from_sql}{where_sql}"
2170        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
2171        return self.prepend_ctes(expression, sql)
2172
2173    def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str:
2174        values_as_table = values_as_table and self.VALUES_AS_TABLE
2175
2176        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
2177        if values_as_table or not expression.find_ancestor(exp.From, exp.Join):
2178            args = self.expressions(expression)
2179            alias = self.sql(expression, "alias")
2180            values = f"VALUES{self.seg('')}{args}"
2181            values = (
2182                f"({values})"
2183                if self.WRAP_DERIVED_VALUES
2184                and (alias or isinstance(expression.parent, (exp.From, exp.Table)))
2185                else values
2186            )
2187            return f"{values} AS {alias}" if alias else values
2188
2189        # Converts `VALUES...` expression into a series of select unions.
2190        alias_node = expression.args.get("alias")
2191        column_names = alias_node and alias_node.columns
2192
2193        selects: t.List[exp.Query] = []
2194
2195        for i, tup in enumerate(expression.expressions):
2196            row = tup.expressions
2197
2198            if i == 0 and column_names:
2199                row = [
2200                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
2201                ]
2202
2203            selects.append(exp.Select(expressions=row))
2204
2205        if self.pretty:
2206            # This may result in poor performance for large-cardinality `VALUES` tables, due to
2207            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
2208            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
2209            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
2210            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
2211
2212        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
2213        unions = " UNION ALL ".join(self.sql(select) for select in selects)
2214        return f"({unions}){alias}"
2215
2216    def var_sql(self, expression: exp.Var) -> str:
2217        return self.sql(expression, "this")
2218
2219    @unsupported_args("expressions")
2220    def into_sql(self, expression: exp.Into) -> str:
2221        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
2222        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
2223        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
2224
2225    def from_sql(self, expression: exp.From) -> str:
2226        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
2227
2228    def groupingsets_sql(self, expression: exp.GroupingSets) -> str:
2229        grouping_sets = self.expressions(expression, indent=False)
2230        return f"GROUPING SETS {self.wrap(grouping_sets)}"
2231
2232    def rollup_sql(self, expression: exp.Rollup) -> str:
2233        expressions = self.expressions(expression, indent=False)
2234        return f"ROLLUP {self.wrap(expressions)}" if expressions else "WITH ROLLUP"
2235
2236    def cube_sql(self, expression: exp.Cube) -> str:
2237        expressions = self.expressions(expression, indent=False)
2238        return f"CUBE {self.wrap(expressions)}" if expressions else "WITH CUBE"
2239
2240    def group_sql(self, expression: exp.Group) -> str:
2241        group_by_all = expression.args.get("all")
2242        if group_by_all is True:
2243            modifier = " ALL"
2244        elif group_by_all is False:
2245            modifier = " DISTINCT"
2246        else:
2247            modifier = ""
2248
2249        group_by = self.op_expressions(f"GROUP BY{modifier}", expression)
2250
2251        grouping_sets = self.expressions(expression, key="grouping_sets")
2252        cube = self.expressions(expression, key="cube")
2253        rollup = self.expressions(expression, key="rollup")
2254
2255        groupings = csv(
2256            self.seg(grouping_sets) if grouping_sets else "",
2257            self.seg(cube) if cube else "",
2258            self.seg(rollup) if rollup else "",
2259            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
2260            sep=self.GROUPINGS_SEP,
2261        )
2262
2263        if (
2264            expression.expressions
2265            and groupings
2266            and groupings.strip() not in ("WITH CUBE", "WITH ROLLUP")
2267        ):
2268            group_by = f"{group_by}{self.GROUPINGS_SEP}"
2269
2270        return f"{group_by}{groupings}"
2271
2272    def having_sql(self, expression: exp.Having) -> str:
2273        this = self.indent(self.sql(expression, "this"))
2274        return f"{self.seg('HAVING')}{self.sep()}{this}"
2275
2276    def connect_sql(self, expression: exp.Connect) -> str:
2277        start = self.sql(expression, "start")
2278        start = self.seg(f"START WITH {start}") if start else ""
2279        nocycle = " NOCYCLE" if expression.args.get("nocycle") else ""
2280        connect = self.sql(expression, "connect")
2281        connect = self.seg(f"CONNECT BY{nocycle} {connect}")
2282        return start + connect
2283
2284    def prior_sql(self, expression: exp.Prior) -> str:
2285        return f"PRIOR {self.sql(expression, 'this')}"
2286
2287    def join_sql(self, expression: exp.Join) -> str:
2288        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
2289            side = None
2290        else:
2291            side = expression.side
2292
2293        op_sql = " ".join(
2294            op
2295            for op in (
2296                expression.method,
2297                "GLOBAL" if expression.args.get("global") else None,
2298                side,
2299                expression.kind,
2300                expression.hint if self.JOIN_HINTS else None,
2301            )
2302            if op
2303        )
2304        match_cond = self.sql(expression, "match_condition")
2305        match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
2306        on_sql = self.sql(expression, "on")
2307        using = expression.args.get("using")
2308
2309        if not on_sql and using:
2310            on_sql = csv(*(self.sql(column) for column in using))
2311
2312        this = expression.this
2313        this_sql = self.sql(this)
2314
2315        exprs = self.expressions(expression)
2316        if exprs:
2317            this_sql = f"{this_sql},{self.seg(exprs)}"
2318
2319        if on_sql:
2320            on_sql = self.indent(on_sql, skip_first=True)
2321            space = self.seg(" " * self.pad) if self.pretty else " "
2322            if using:
2323                on_sql = f"{space}USING ({on_sql})"
2324            else:
2325                on_sql = f"{space}ON {on_sql}"
2326        elif not op_sql:
2327            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
2328                return f" {this_sql}"
2329
2330            return f", {this_sql}"
2331
2332        if op_sql != "STRAIGHT_JOIN":
2333            op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
2334
2335        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2336        return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}{pivots}"
2337
2338    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->", wrap: bool = True) -> str:
2339        args = self.expressions(expression, flat=True)
2340        args = f"({args})" if wrap and len(args.split(",")) > 1 else args
2341        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
2342
2343    def lateral_op(self, expression: exp.Lateral) -> str:
2344        cross_apply = expression.args.get("cross_apply")
2345
2346        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
2347        if cross_apply is True:
2348            op = "INNER JOIN "
2349        elif cross_apply is False:
2350            op = "LEFT JOIN "
2351        else:
2352            op = ""
2353
2354        return f"{op}LATERAL"
2355
2356    def lateral_sql(self, expression: exp.Lateral) -> str:
2357        this = self.sql(expression, "this")
2358
2359        if expression.args.get("view"):
2360            alias = expression.args["alias"]
2361            columns = self.expressions(alias, key="columns", flat=True)
2362            table = f" {alias.name}" if alias.name else ""
2363            columns = f" AS {columns}" if columns else ""
2364            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
2365            return f"{op_sql}{self.sep()}{this}{table}{columns}"
2366
2367        alias = self.sql(expression, "alias")
2368        alias = f" AS {alias}" if alias else ""
2369
2370        ordinality = expression.args.get("ordinality") or ""
2371        if ordinality:
2372            ordinality = f" WITH ORDINALITY{alias}"
2373            alias = ""
2374
2375        return f"{self.lateral_op(expression)} {this}{alias}{ordinality}"
2376
2377    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
2378        this = self.sql(expression, "this")
2379
2380        args = [
2381            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
2382            for e in (expression.args.get(k) for k in ("offset", "expression"))
2383            if e
2384        ]
2385
2386        args_sql = ", ".join(self.sql(e) for e in args)
2387        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
2388        expressions = self.expressions(expression, flat=True)
2389        limit_options = self.sql(expression, "limit_options")
2390        expressions = f" BY {expressions}" if expressions else ""
2391
2392        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{limit_options}{expressions}"
2393
2394    def offset_sql(self, expression: exp.Offset) -> str:
2395        this = self.sql(expression, "this")
2396        value = expression.expression
2397        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
2398        expressions = self.expressions(expression, flat=True)
2399        expressions = f" BY {expressions}" if expressions else ""
2400        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
2401
2402    def setitem_sql(self, expression: exp.SetItem) -> str:
2403        kind = self.sql(expression, "kind")
2404        kind = f"{kind} " if kind else ""
2405        this = self.sql(expression, "this")
2406        expressions = self.expressions(expression)
2407        collate = self.sql(expression, "collate")
2408        collate = f" COLLATE {collate}" if collate else ""
2409        global_ = "GLOBAL " if expression.args.get("global") else ""
2410        return f"{global_}{kind}{this}{expressions}{collate}"
2411
2412    def set_sql(self, expression: exp.Set) -> str:
2413        expressions = f" {self.expressions(expression, flat=True)}"
2414        tag = " TAG" if expression.args.get("tag") else ""
2415        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
2416
2417    def queryband_sql(self, expression: exp.QueryBand) -> str:
2418        this = self.sql(expression, "this")
2419        update = " UPDATE" if expression.args.get("update") else ""
2420        scope = self.sql(expression, "scope")
2421        scope = f" FOR {scope}" if scope else ""
2422
2423        return f"QUERY_BAND = {this}{update}{scope}"
2424
2425    def pragma_sql(self, expression: exp.Pragma) -> str:
2426        return f"PRAGMA {self.sql(expression, 'this')}"
2427
2428    def lock_sql(self, expression: exp.Lock) -> str:
2429        if not self.LOCKING_READS_SUPPORTED:
2430            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
2431            return ""
2432
2433        update = expression.args["update"]
2434        key = expression.args.get("key")
2435        if update:
2436            lock_type = "FOR NO KEY UPDATE" if key else "FOR UPDATE"
2437        else:
2438            lock_type = "FOR KEY SHARE" if key else "FOR SHARE"
2439        expressions = self.expressions(expression, flat=True)
2440        expressions = f" OF {expressions}" if expressions else ""
2441        wait = expression.args.get("wait")
2442
2443        if wait is not None:
2444            if isinstance(wait, exp.Literal):
2445                wait = f" WAIT {self.sql(wait)}"
2446            else:
2447                wait = " NOWAIT" if wait else " SKIP LOCKED"
2448
2449        return f"{lock_type}{expressions}{wait or ''}"
2450
2451    def literal_sql(self, expression: exp.Literal) -> str:
2452        text = expression.this or ""
2453        if expression.is_string:
2454            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
2455        return text
2456
2457    def escape_str(self, text: str, escape_backslash: bool = True) -> str:
2458        if self.dialect.ESCAPED_SEQUENCES:
2459            to_escaped = self.dialect.ESCAPED_SEQUENCES
2460            text = "".join(
2461                to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text
2462            )
2463
2464        return self._replace_line_breaks(text).replace(
2465            self.dialect.QUOTE_END, self._escaped_quote_end
2466        )
2467
2468    def loaddata_sql(self, expression: exp.LoadData) -> str:
2469        local = " LOCAL" if expression.args.get("local") else ""
2470        inpath = f" INPATH {self.sql(expression, 'inpath')}"
2471        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
2472        this = f" INTO TABLE {self.sql(expression, 'this')}"
2473        partition = self.sql(expression, "partition")
2474        partition = f" {partition}" if partition else ""
2475        input_format = self.sql(expression, "input_format")
2476        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
2477        serde = self.sql(expression, "serde")
2478        serde = f" SERDE {serde}" if serde else ""
2479        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
2480
2481    def null_sql(self, *_) -> str:
2482        return "NULL"
2483
2484    def boolean_sql(self, expression: exp.Boolean) -> str:
2485        return "TRUE" if expression.this else "FALSE"
2486
2487    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
2488        this = self.sql(expression, "this")
2489        this = f"{this} " if this else this
2490        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
2491        return self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
2492
2493    def withfill_sql(self, expression: exp.WithFill) -> str:
2494        from_sql = self.sql(expression, "from")
2495        from_sql = f" FROM {from_sql}" if from_sql else ""
2496        to_sql = self.sql(expression, "to")
2497        to_sql = f" TO {to_sql}" if to_sql else ""
2498        step_sql = self.sql(expression, "step")
2499        step_sql = f" STEP {step_sql}" if step_sql else ""
2500        interpolated_values = [
2501            f"{self.sql(e, 'alias')} AS {self.sql(e, 'this')}"
2502            if isinstance(e, exp.Alias)
2503            else self.sql(e, "this")
2504            for e in expression.args.get("interpolate") or []
2505        ]
2506        interpolate = (
2507            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
2508        )
2509        return f"WITH FILL{from_sql}{to_sql}{step_sql}{interpolate}"
2510
2511    def cluster_sql(self, expression: exp.Cluster) -> str:
2512        return self.op_expressions("CLUSTER BY", expression)
2513
2514    def distribute_sql(self, expression: exp.Distribute) -> str:
2515        return self.op_expressions("DISTRIBUTE BY", expression)
2516
2517    def sort_sql(self, expression: exp.Sort) -> str:
2518        return self.op_expressions("SORT BY", expression)
2519
2520    def ordered_sql(self, expression: exp.Ordered) -> str:
2521        desc = expression.args.get("desc")
2522        asc = not desc
2523
2524        nulls_first = expression.args.get("nulls_first")
2525        nulls_last = not nulls_first
2526        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
2527        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
2528        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
2529
2530        this = self.sql(expression, "this")
2531
2532        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
2533        nulls_sort_change = ""
2534        if nulls_first and (
2535            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
2536        ):
2537            nulls_sort_change = " NULLS FIRST"
2538        elif (
2539            nulls_last
2540            and ((asc and nulls_are_small) or (desc and nulls_are_large))
2541            and not nulls_are_last
2542        ):
2543            nulls_sort_change = " NULLS LAST"
2544
2545        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
2546        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
2547            window = expression.find_ancestor(exp.Window, exp.Select)
2548            if isinstance(window, exp.Window) and window.args.get("spec"):
2549                self.unsupported(
2550                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
2551                )
2552                nulls_sort_change = ""
2553            elif self.NULL_ORDERING_SUPPORTED is False and (
2554                (asc and nulls_sort_change == " NULLS LAST")
2555                or (desc and nulls_sort_change == " NULLS FIRST")
2556            ):
2557                # BigQuery does not allow these ordering/nulls combinations when used under
2558                # an aggregation func or under a window containing one
2559                ancestor = expression.find_ancestor(exp.AggFunc, exp.Window, exp.Select)
2560
2561                if isinstance(ancestor, exp.Window):
2562                    ancestor = ancestor.this
2563                if isinstance(ancestor, exp.AggFunc):
2564                    self.unsupported(
2565                        f"'{nulls_sort_change.strip()}' translation not supported for aggregate functions with {sort_order} sort order"
2566                    )
2567                    nulls_sort_change = ""
2568            elif self.NULL_ORDERING_SUPPORTED is None:
2569                if expression.this.is_int:
2570                    self.unsupported(
2571                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
2572                    )
2573                elif not isinstance(expression.this, exp.Rand):
2574                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
2575                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
2576                nulls_sort_change = ""
2577
2578        with_fill = self.sql(expression, "with_fill")
2579        with_fill = f" {with_fill}" if with_fill else ""
2580
2581        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
2582
2583    def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str:
2584        window_frame = self.sql(expression, "window_frame")
2585        window_frame = f"{window_frame} " if window_frame else ""
2586
2587        this = self.sql(expression, "this")
2588
2589        return f"{window_frame}{this}"
2590
2591    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
2592        partition = self.partition_by_sql(expression)
2593        order = self.sql(expression, "order")
2594        measures = self.expressions(expression, key="measures")
2595        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
2596        rows = self.sql(expression, "rows")
2597        rows = self.seg(rows) if rows else ""
2598        after = self.sql(expression, "after")
2599        after = self.seg(after) if after else ""
2600        pattern = self.sql(expression, "pattern")
2601        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
2602        definition_sqls = [
2603            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
2604            for definition in expression.args.get("define", [])
2605        ]
2606        definitions = self.expressions(sqls=definition_sqls)
2607        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
2608        body = "".join(
2609            (
2610                partition,
2611                order,
2612                measures,
2613                rows,
2614                after,
2615                pattern,
2616                define,
2617            )
2618        )
2619        alias = self.sql(expression, "alias")
2620        alias = f" {alias}" if alias else ""
2621        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
2622
2623    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
2624        limit = expression.args.get("limit")
2625
2626        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
2627            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
2628        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
2629            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
2630
2631        return csv(
2632            *sqls,
2633            *[self.sql(join) for join in expression.args.get("joins") or []],
2634            self.sql(expression, "match"),
2635            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
2636            self.sql(expression, "prewhere"),
2637            self.sql(expression, "where"),
2638            self.sql(expression, "connect"),
2639            self.sql(expression, "group"),
2640            self.sql(expression, "having"),
2641            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
2642            self.sql(expression, "order"),
2643            *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
2644            *self.after_limit_modifiers(expression),
2645            self.options_modifier(expression),
2646            self.for_modifiers(expression),
2647            sep="",
2648        )
2649
2650    def options_modifier(self, expression: exp.Expression) -> str:
2651        options = self.expressions(expression, key="options")
2652        return f" {options}" if options else ""
2653
2654    def for_modifiers(self, expression: exp.Expression) -> str:
2655        for_modifiers = self.expressions(expression, key="for")
2656        return f"{self.sep()}FOR XML{self.seg(for_modifiers)}" if for_modifiers else ""
2657
2658    def queryoption_sql(self, expression: exp.QueryOption) -> str:
2659        self.unsupported("Unsupported query option.")
2660        return ""
2661
2662    def offset_limit_modifiers(
2663        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
2664    ) -> t.List[str]:
2665        return [
2666            self.sql(expression, "offset") if fetch else self.sql(limit),
2667            self.sql(limit) if fetch else self.sql(expression, "offset"),
2668        ]
2669
2670    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
2671        locks = self.expressions(expression, key="locks", sep=" ")
2672        locks = f" {locks}" if locks else ""
2673        return [locks, self.sql(expression, "sample")]
2674
2675    def select_sql(self, expression: exp.Select) -> str:
2676        into = expression.args.get("into")
2677        if not self.SUPPORTS_SELECT_INTO and into:
2678            into.pop()
2679
2680        hint = self.sql(expression, "hint")
2681        distinct = self.sql(expression, "distinct")
2682        distinct = f" {distinct}" if distinct else ""
2683        kind = self.sql(expression, "kind")
2684
2685        limit = expression.args.get("limit")
2686        if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP:
2687            top = self.limit_sql(limit, top=True)
2688            limit.pop()
2689        else:
2690            top = ""
2691
2692        expressions = self.expressions(expression)
2693
2694        if kind:
2695            if kind in self.SELECT_KINDS:
2696                kind = f" AS {kind}"
2697            else:
2698                if kind == "STRUCT":
2699                    expressions = self.expressions(
2700                        sqls=[
2701                            self.sql(
2702                                exp.Struct(
2703                                    expressions=[
2704                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
2705                                        if isinstance(e, exp.Alias)
2706                                        else e
2707                                        for e in expression.expressions
2708                                    ]
2709                                )
2710                            )
2711                        ]
2712                    )
2713                kind = ""
2714
2715        operation_modifiers = self.expressions(expression, key="operation_modifiers", sep=" ")
2716        operation_modifiers = f"{self.sep()}{operation_modifiers}" if operation_modifiers else ""
2717
2718        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
2719        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
2720        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
2721        expressions = f"{self.sep()}{expressions}" if expressions else expressions
2722        sql = self.query_modifiers(
2723            expression,
2724            f"SELECT{top_distinct}{operation_modifiers}{kind}{expressions}",
2725            self.sql(expression, "into", comment=False),
2726            self.sql(expression, "from", comment=False),
2727        )
2728
2729        # If both the CTE and SELECT clauses have comments, generate the latter earlier
2730        if expression.args.get("with"):
2731            sql = self.maybe_comment(sql, expression)
2732            expression.pop_comments()
2733
2734        sql = self.prepend_ctes(expression, sql)
2735
2736        if not self.SUPPORTS_SELECT_INTO and into:
2737            if into.args.get("temporary"):
2738                table_kind = " TEMPORARY"
2739            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
2740                table_kind = " UNLOGGED"
2741            else:
2742                table_kind = ""
2743            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
2744
2745        return sql
2746
2747    def schema_sql(self, expression: exp.Schema) -> str:
2748        this = self.sql(expression, "this")
2749        sql = self.schema_columns_sql(expression)
2750        return f"{this} {sql}" if this and sql else this or sql
2751
2752    def schema_columns_sql(self, expression: exp.Schema) -> str:
2753        if expression.expressions:
2754            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
2755        return ""
2756
2757    def star_sql(self, expression: exp.Star) -> str:
2758        except_ = self.expressions(expression, key="except", flat=True)
2759        except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
2760        replace = self.expressions(expression, key="replace", flat=True)
2761        replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
2762        rename = self.expressions(expression, key="rename", flat=True)
2763        rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
2764        return f"*{except_}{replace}{rename}"
2765
2766    def parameter_sql(self, expression: exp.Parameter) -> str:
2767        this = self.sql(expression, "this")
2768        return f"{self.PARAMETER_TOKEN}{this}"
2769
2770    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
2771        this = self.sql(expression, "this")
2772        kind = expression.text("kind")
2773        if kind:
2774            kind = f"{kind}."
2775        return f"@@{kind}{this}"
2776
2777    def placeholder_sql(self, expression: exp.Placeholder) -> str:
2778        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?"
2779
2780    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
2781        alias = self.sql(expression, "alias")
2782        alias = f"{sep}{alias}" if alias else ""
2783        sample = self.sql(expression, "sample")
2784        if self.dialect.ALIAS_POST_TABLESAMPLE and sample:
2785            alias = f"{sample}{alias}"
2786
2787            # Set to None so it's not generated again by self.query_modifiers()
2788            expression.set("sample", None)
2789
2790        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2791        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
2792        return self.prepend_ctes(expression, sql)
2793
2794    def qualify_sql(self, expression: exp.Qualify) -> str:
2795        this = self.indent(self.sql(expression, "this"))
2796        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
2797
2798    def unnest_sql(self, expression: exp.Unnest) -> str:
2799        args = self.expressions(expression, flat=True)
2800
2801        alias = expression.args.get("alias")
2802        offset = expression.args.get("offset")
2803
2804        if self.UNNEST_WITH_ORDINALITY:
2805            if alias and isinstance(offset, exp.Expression):
2806                alias.append("columns", offset)
2807
2808        if alias and self.dialect.UNNEST_COLUMN_ONLY:
2809            columns = alias.columns
2810            alias = self.sql(columns[0]) if columns else ""
2811        else:
2812            alias = self.sql(alias)
2813
2814        alias = f" AS {alias}" if alias else alias
2815        if self.UNNEST_WITH_ORDINALITY:
2816            suffix = f" WITH ORDINALITY{alias}" if offset else alias
2817        else:
2818            if isinstance(offset, exp.Expression):
2819                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
2820            elif offset:
2821                suffix = f"{alias} WITH OFFSET"
2822            else:
2823                suffix = alias
2824
2825        return f"UNNEST({args}){suffix}"
2826
2827    def prewhere_sql(self, expression: exp.PreWhere) -> str:
2828        return ""
2829
2830    def where_sql(self, expression: exp.Where) -> str:
2831        this = self.indent(self.sql(expression, "this"))
2832        return f"{self.seg('WHERE')}{self.sep()}{this}"
2833
2834    def window_sql(self, expression: exp.Window) -> str:
2835        this = self.sql(expression, "this")
2836        partition = self.partition_by_sql(expression)
2837        order = expression.args.get("order")
2838        order = self.order_sql(order, flat=True) if order else ""
2839        spec = self.sql(expression, "spec")
2840        alias = self.sql(expression, "alias")
2841        over = self.sql(expression, "over") or "OVER"
2842
2843        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
2844
2845        first = expression.args.get("first")
2846        if first is None:
2847            first = ""
2848        else:
2849            first = "FIRST" if first else "LAST"
2850
2851        if not partition and not order and not spec and alias:
2852            return f"{this} {alias}"
2853
2854        args = self.format_args(
2855            *[arg for arg in (alias, first, partition, order, spec) if arg], sep=" "
2856        )
2857        return f"{this} ({args})"
2858
2859    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
2860        partition = self.expressions(expression, key="partition_by", flat=True)
2861        return f"PARTITION BY {partition}" if partition else ""
2862
2863    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
2864        kind = self.sql(expression, "kind")
2865        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
2866        end = (
2867            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
2868            or "CURRENT ROW"
2869        )
2870
2871        window_spec = f"{kind} BETWEEN {start} AND {end}"
2872
2873        exclude = self.sql(expression, "exclude")
2874        if exclude:
2875            if self.SUPPORTS_WINDOW_EXCLUDE:
2876                window_spec += f" EXCLUDE {exclude}"
2877            else:
2878                self.unsupported("EXCLUDE clause is not supported in the WINDOW clause")
2879
2880        return window_spec
2881
2882    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
2883        this = self.sql(expression, "this")
2884        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
2885        return f"{this} WITHIN GROUP ({expression_sql})"
2886
2887    def between_sql(self, expression: exp.Between) -> str:
2888        this = self.sql(expression, "this")
2889        low = self.sql(expression, "low")
2890        high = self.sql(expression, "high")
2891        symmetric = expression.args.get("symmetric")
2892
2893        if symmetric and not self.SUPPORTS_BETWEEN_FLAGS:
2894            return f"({this} BETWEEN {low} AND {high} OR {this} BETWEEN {high} AND {low})"
2895
2896        flag = (
2897            " SYMMETRIC"
2898            if symmetric
2899            else " ASYMMETRIC"
2900            if symmetric is False and self.SUPPORTS_BETWEEN_FLAGS
2901            else ""  # silently drop ASYMMETRIC – semantics identical
2902        )
2903        return f"{this} BETWEEN{flag} {low} AND {high}"
2904
2905    def bracket_offset_expressions(
2906        self, expression: exp.Bracket, index_offset: t.Optional[int] = None
2907    ) -> t.List[exp.Expression]:
2908        return apply_index_offset(
2909            expression.this,
2910            expression.expressions,
2911            (index_offset or self.dialect.INDEX_OFFSET) - expression.args.get("offset", 0),
2912            dialect=self.dialect,
2913        )
2914
2915    def bracket_sql(self, expression: exp.Bracket) -> str:
2916        expressions = self.bracket_offset_expressions(expression)
2917        expressions_sql = ", ".join(self.sql(e) for e in expressions)
2918        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
2919
2920    def all_sql(self, expression: exp.All) -> str:
2921        this = self.sql(expression, "this")
2922        if not isinstance(expression.this, (exp.Tuple, exp.Paren)):
2923            this = self.wrap(this)
2924        return f"ALL {this}"
2925
2926    def any_sql(self, expression: exp.Any) -> str:
2927        this = self.sql(expression, "this")
2928        if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)):
2929            if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
2930                this = self.wrap(this)
2931            return f"ANY{this}"
2932        return f"ANY {this}"
2933
2934    def exists_sql(self, expression: exp.Exists) -> str:
2935        return f"EXISTS{self.wrap(expression)}"
2936
2937    def case_sql(self, expression: exp.Case) -> str:
2938        this = self.sql(expression, "this")
2939        statements = [f"CASE {this}" if this else "CASE"]
2940
2941        for e in expression.args["ifs"]:
2942            statements.append(f"WHEN {self.sql(e, 'this')}")
2943            statements.append(f"THEN {self.sql(e, 'true')}")
2944
2945        default = self.sql(expression, "default")
2946
2947        if default:
2948            statements.append(f"ELSE {default}")
2949
2950        statements.append("END")
2951
2952        if self.pretty and self.too_wide(statements):
2953            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
2954
2955        return " ".join(statements)
2956
2957    def constraint_sql(self, expression: exp.Constraint) -> str:
2958        this = self.sql(expression, "this")
2959        expressions = self.expressions(expression, flat=True)
2960        return f"CONSTRAINT {this} {expressions}"
2961
2962    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
2963        order = expression.args.get("order")
2964        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
2965        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
2966
2967    def extract_sql(self, expression: exp.Extract) -> str:
2968        from sqlglot.dialects.dialect import map_date_part
2969
2970        this = (
2971            map_date_part(expression.this, self.dialect)
2972            if self.NORMALIZE_EXTRACT_DATE_PARTS
2973            else expression.this
2974        )
2975        this_sql = self.sql(this) if self.EXTRACT_ALLOWS_QUOTES else this.name
2976        expression_sql = self.sql(expression, "expression")
2977
2978        return f"EXTRACT({this_sql} FROM {expression_sql})"
2979
2980    def trim_sql(self, expression: exp.Trim) -> str:
2981        trim_type = self.sql(expression, "position")
2982
2983        if trim_type == "LEADING":
2984            func_name = "LTRIM"
2985        elif trim_type == "TRAILING":
2986            func_name = "RTRIM"
2987        else:
2988            func_name = "TRIM"
2989
2990        return self.func(func_name, expression.this, expression.expression)
2991
2992    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
2993        args = expression.expressions
2994        if isinstance(expression, exp.ConcatWs):
2995            args = args[1:]  # Skip the delimiter
2996
2997        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2998            args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args]
2999
3000        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
3001
3002            def _wrap_with_coalesce(e: exp.Expression) -> exp.Expression:
3003                if not e.type:
3004                    from sqlglot.optimizer.annotate_types import annotate_types
3005
3006                    e = annotate_types(e, dialect=self.dialect)
3007
3008                if e.is_string or e.is_type(exp.DataType.Type.ARRAY):
3009                    return e
3010
3011                return exp.func("coalesce", e, exp.Literal.string(""))
3012
3013            args = [_wrap_with_coalesce(e) for e in args]
3014
3015        return args
3016
3017    def concat_sql(self, expression: exp.Concat) -> str:
3018        expressions = self.convert_concat_args(expression)
3019
3020        # Some dialects don't allow a single-argument CONCAT call
3021        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
3022            return self.sql(expressions[0])
3023
3024        return self.func("CONCAT", *expressions)
3025
3026    def concatws_sql(self, expression: exp.ConcatWs) -> str:
3027        return self.func(
3028            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
3029        )
3030
3031    def check_sql(self, expression: exp.Check) -> str:
3032        this = self.sql(expression, key="this")
3033        return f"CHECK ({this})"
3034
3035    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
3036        expressions = self.expressions(expression, flat=True)
3037        expressions = f" ({expressions})" if expressions else ""
3038        reference = self.sql(expression, "reference")
3039        reference = f" {reference}" if reference else ""
3040        delete = self.sql(expression, "delete")
3041        delete = f" ON DELETE {delete}" if delete else ""
3042        update = self.sql(expression, "update")
3043        update = f" ON UPDATE {update}" if update else ""
3044        options = self.expressions(expression, key="options", flat=True, sep=" ")
3045        options = f" {options}" if options else ""
3046        return f"FOREIGN KEY{expressions}{reference}{delete}{update}{options}"
3047
3048    def primarykey_sql(self, expression: exp.PrimaryKey) -> str:
3049        expressions = self.expressions(expression, flat=True)
3050        include = self.sql(expression, "include")
3051        options = self.expressions(expression, key="options", flat=True, sep=" ")
3052        options = f" {options}" if options else ""
3053        return f"PRIMARY KEY ({expressions}){include}{options}"
3054
3055    def if_sql(self, expression: exp.If) -> str:
3056        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
3057
3058    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
3059        modifier = expression.args.get("modifier")
3060        modifier = f" {modifier}" if modifier else ""
3061        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
3062
3063    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
3064        return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"
3065
3066    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
3067        path = self.expressions(expression, sep="", flat=True).lstrip(".")
3068
3069        if expression.args.get("escape"):
3070            path = self.escape_str(path)
3071
3072        if self.QUOTE_JSON_PATH:
3073            path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
3074
3075        return path
3076
3077    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
3078        if isinstance(expression, exp.JSONPathPart):
3079            transform = self.TRANSFORMS.get(expression.__class__)
3080            if not callable(transform):
3081                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
3082                return ""
3083
3084            return transform(self, expression)
3085
3086        if isinstance(expression, int):
3087            return str(expression)
3088
3089        if self._quote_json_path_key_using_brackets and self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
3090            escaped = expression.replace("'", "\\'")
3091            escaped = f"\\'{expression}\\'"
3092        else:
3093            escaped = expression.replace('"', '\\"')
3094            escaped = f'"{escaped}"'
3095
3096        return escaped
3097
3098    def formatjson_sql(self, expression: exp.FormatJson) -> str:
3099        return f"{self.sql(expression, 'this')} FORMAT JSON"
3100
3101    def formatphrase_sql(self, expression: exp.FormatPhrase) -> str:
3102        # Output the Teradata column FORMAT override.
3103        # https://docs.teradata.com/r/Enterprise_IntelliFlex_VMware/SQL-Data-Types-and-Literals/Data-Type-Formats-and-Format-Phrases/FORMAT
3104        this = self.sql(expression, "this")
3105        fmt = self.sql(expression, "format")
3106        return f"{this} (FORMAT {fmt})"
3107
3108    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
3109        null_handling = expression.args.get("null_handling")
3110        null_handling = f" {null_handling}" if null_handling else ""
3111
3112        unique_keys = expression.args.get("unique_keys")
3113        if unique_keys is not None:
3114            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
3115        else:
3116            unique_keys = ""
3117
3118        return_type = self.sql(expression, "return_type")
3119        return_type = f" RETURNING {return_type}" if return_type else ""
3120        encoding = self.sql(expression, "encoding")
3121        encoding = f" ENCODING {encoding}" if encoding else ""
3122
3123        return self.func(
3124            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
3125            *expression.expressions,
3126            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
3127        )
3128
3129    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
3130        return self.jsonobject_sql(expression)
3131
3132    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
3133        null_handling = expression.args.get("null_handling")
3134        null_handling = f" {null_handling}" if null_handling else ""
3135        return_type = self.sql(expression, "return_type")
3136        return_type = f" RETURNING {return_type}" if return_type else ""
3137        strict = " STRICT" if expression.args.get("strict") else ""
3138        return self.func(
3139            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
3140        )
3141
3142    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
3143        this = self.sql(expression, "this")
3144        order = self.sql(expression, "order")
3145        null_handling = expression.args.get("null_handling")
3146        null_handling = f" {null_handling}" if null_handling else ""
3147        return_type = self.sql(expression, "return_type")
3148        return_type = f" RETURNING {return_type}" if return_type else ""
3149        strict = " STRICT" if expression.args.get("strict") else ""
3150        return self.func(
3151            "JSON_ARRAYAGG",
3152            this,
3153            suffix=f"{order}{null_handling}{return_type}{strict})",
3154        )
3155
3156    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
3157        path = self.sql(expression, "path")
3158        path = f" PATH {path}" if path else ""
3159        nested_schema = self.sql(expression, "nested_schema")
3160
3161        if nested_schema:
3162            return f"NESTED{path} {nested_schema}"
3163
3164        this = self.sql(expression, "this")
3165        kind = self.sql(expression, "kind")
3166        kind = f" {kind}" if kind else ""
3167        return f"{this}{kind}{path}"
3168
3169    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
3170        return self.func("COLUMNS", *expression.expressions)
3171
3172    def jsontable_sql(self, expression: exp.JSONTable) -> str:
3173        this = self.sql(expression, "this")
3174        path = self.sql(expression, "path")
3175        path = f", {path}" if path else ""
3176        error_handling = expression.args.get("error_handling")
3177        error_handling = f" {error_handling}" if error_handling else ""
3178        empty_handling = expression.args.get("empty_handling")
3179        empty_handling = f" {empty_handling}" if empty_handling else ""
3180        schema = self.sql(expression, "schema")
3181        return self.func(
3182            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
3183        )
3184
3185    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
3186        this = self.sql(expression, "this")
3187        kind = self.sql(expression, "kind")
3188        path = self.sql(expression, "path")
3189        path = f" {path}" if path else ""
3190        as_json = " AS JSON" if expression.args.get("as_json") else ""
3191        return f"{this} {kind}{path}{as_json}"
3192
3193    def openjson_sql(self, expression: exp.OpenJSON) -> str:
3194        this = self.sql(expression, "this")
3195        path = self.sql(expression, "path")
3196        path = f", {path}" if path else ""
3197        expressions = self.expressions(expression)
3198        with_ = (
3199            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
3200            if expressions
3201            else ""
3202        )
3203        return f"OPENJSON({this}{path}){with_}"
3204
3205    def in_sql(self, expression: exp.In) -> str:
3206        query = expression.args.get("query")
3207        unnest = expression.args.get("unnest")
3208        field = expression.args.get("field")
3209        is_global = " GLOBAL" if expression.args.get("is_global") else ""
3210
3211        if query:
3212            in_sql = self.sql(query)
3213        elif unnest:
3214            in_sql = self.in_unnest_op(unnest)
3215        elif field:
3216            in_sql = self.sql(field)
3217        else:
3218            in_sql = f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
3219
3220        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
3221
3222    def in_unnest_op(self, unnest: exp.Unnest) -> str:
3223        return f"(SELECT {self.sql(unnest)})"
3224
3225    def interval_sql(self, expression: exp.Interval) -> str:
3226        unit = self.sql(expression, "unit")
3227        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
3228            unit = self.TIME_PART_SINGULARS.get(unit, unit)
3229        unit = f" {unit}" if unit else ""
3230
3231        if self.SINGLE_STRING_INTERVAL:
3232            this = expression.this.name if expression.this else ""
3233            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
3234
3235        this = self.sql(expression, "this")
3236        if this:
3237            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
3238            this = f" {this}" if unwrapped else f" ({this})"
3239
3240        return f"INTERVAL{this}{unit}"
3241
3242    def return_sql(self, expression: exp.Return) -> str:
3243        return f"RETURN {self.sql(expression, 'this')}"
3244
3245    def reference_sql(self, expression: exp.Reference) -> str:
3246        this = self.sql(expression, "this")
3247        expressions = self.expressions(expression, flat=True)
3248        expressions = f"({expressions})" if expressions else ""
3249        options = self.expressions(expression, key="options", flat=True, sep=" ")
3250        options = f" {options}" if options else ""
3251        return f"REFERENCES {this}{expressions}{options}"
3252
3253    def anonymous_sql(self, expression: exp.Anonymous) -> str:
3254        # We don't normalize qualified functions such as a.b.foo(), because they can be case-sensitive
3255        parent = expression.parent
3256        is_qualified = isinstance(parent, exp.Dot) and expression is parent.expression
3257        return self.func(
3258            self.sql(expression, "this"), *expression.expressions, normalize=not is_qualified
3259        )
3260
3261    def paren_sql(self, expression: exp.Paren) -> str:
3262        sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
3263        return f"({sql}{self.seg(')', sep='')}"
3264
3265    def neg_sql(self, expression: exp.Neg) -> str:
3266        # This makes sure we don't convert "- - 5" to "--5", which is a comment
3267        this_sql = self.sql(expression, "this")
3268        sep = " " if this_sql[0] == "-" else ""
3269        return f"-{sep}{this_sql}"
3270
3271    def not_sql(self, expression: exp.Not) -> str:
3272        return f"NOT {self.sql(expression, 'this')}"
3273
3274    def alias_sql(self, expression: exp.Alias) -> str:
3275        alias = self.sql(expression, "alias")
3276        alias = f" AS {alias}" if alias else ""
3277        return f"{self.sql(expression, 'this')}{alias}"
3278
3279    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
3280        alias = expression.args["alias"]
3281
3282        parent = expression.parent
3283        pivot = parent and parent.parent
3284
3285        if isinstance(pivot, exp.Pivot) and pivot.unpivot:
3286            identifier_alias = isinstance(alias, exp.Identifier)
3287            literal_alias = isinstance(alias, exp.Literal)
3288
3289            if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
3290                alias.replace(exp.Literal.string(alias.output_name))
3291            elif not identifier_alias and literal_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
3292                alias.replace(exp.to_identifier(alias.output_name))
3293
3294        return self.alias_sql(expression)
3295
3296    def aliases_sql(self, expression: exp.Aliases) -> str:
3297        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
3298
3299    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
3300        this = self.sql(expression, "this")
3301        index = self.sql(expression, "expression")
3302        return f"{this} AT {index}"
3303
3304    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
3305        this = self.sql(expression, "this")
3306        zone = self.sql(expression, "zone")
3307        return f"{this} AT TIME ZONE {zone}"
3308
3309    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
3310        this = self.sql(expression, "this")
3311        zone = self.sql(expression, "zone")
3312        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
3313
3314    def add_sql(self, expression: exp.Add) -> str:
3315        return self.binary(expression, "+")
3316
3317    def and_sql(
3318        self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None
3319    ) -> str:
3320        return self.connector_sql(expression, "AND", stack)
3321
3322    def or_sql(
3323        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
3324    ) -> str:
3325        return self.connector_sql(expression, "OR", stack)
3326
3327    def xor_sql(
3328        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
3329    ) -> str:
3330        return self.connector_sql(expression, "XOR", stack)
3331
3332    def connector_sql(
3333        self,
3334        expression: exp.Connector,
3335        op: str,
3336        stack: t.Optional[t.List[str | exp.Expression]] = None,
3337    ) -> str:
3338        if stack is not None:
3339            if expression.expressions:
3340                stack.append(self.expressions(expression, sep=f" {op} "))
3341            else:
3342                stack.append(expression.right)
3343                if expression.comments and self.comments:
3344                    for comment in expression.comments:
3345                        if comment:
3346                            op += f" /*{self.sanitize_comment(comment)}*/"
3347                stack.extend((op, expression.left))
3348            return op
3349
3350        stack = [expression]
3351        sqls: t.List[str] = []
3352        ops = set()
3353
3354        while stack:
3355            node = stack.pop()
3356            if isinstance(node, exp.Connector):
3357                ops.add(getattr(self, f"{node.key}_sql")(node, stack))
3358            else:
3359                sql = self.sql(node)
3360                if sqls and sqls[-1] in ops:
3361                    sqls[-1] += f" {sql}"
3362                else:
3363                    sqls.append(sql)
3364
3365        sep = "\n" if self.pretty and self.too_wide(sqls) else " "
3366        return sep.join(sqls)
3367
3368    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
3369        return self.binary(expression, "&")
3370
3371    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
3372        return self.binary(expression, "<<")
3373
3374    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
3375        return f"~{self.sql(expression, 'this')}"
3376
3377    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
3378        return self.binary(expression, "|")
3379
3380    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
3381        return self.binary(expression, ">>")
3382
3383    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
3384        return self.binary(expression, "^")
3385
3386    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
3387        format_sql = self.sql(expression, "format")
3388        format_sql = f" FORMAT {format_sql}" if format_sql else ""
3389        to_sql = self.sql(expression, "to")
3390        to_sql = f" {to_sql}" if to_sql else ""
3391        action = self.sql(expression, "action")
3392        action = f" {action}" if action else ""
3393        default = self.sql(expression, "default")
3394        default = f" DEFAULT {default} ON CONVERSION ERROR" if default else ""
3395        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{default}{format_sql}{action})"
3396
3397    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
3398        zone = self.sql(expression, "this")
3399        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
3400
3401    def collate_sql(self, expression: exp.Collate) -> str:
3402        if self.COLLATE_IS_FUNC:
3403            return self.function_fallback_sql(expression)
3404        return self.binary(expression, "COLLATE")
3405
3406    def command_sql(self, expression: exp.Command) -> str:
3407        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
3408
3409    def comment_sql(self, expression: exp.Comment) -> str:
3410        this = self.sql(expression, "this")
3411        kind = expression.args["kind"]
3412        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
3413        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
3414        expression_sql = self.sql(expression, "expression")
3415        return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"
3416
3417    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
3418        this = self.sql(expression, "this")
3419        delete = " DELETE" if expression.args.get("delete") else ""
3420        recompress = self.sql(expression, "recompress")
3421        recompress = f" RECOMPRESS {recompress}" if recompress else ""
3422        to_disk = self.sql(expression, "to_disk")
3423        to_disk = f" TO DISK {to_disk}" if to_disk else ""
3424        to_volume = self.sql(expression, "to_volume")
3425        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
3426        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
3427
3428    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
3429        where = self.sql(expression, "where")
3430        group = self.sql(expression, "group")
3431        aggregates = self.expressions(expression, key="aggregates")
3432        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
3433
3434        if not (where or group or aggregates) and len(expression.expressions) == 1:
3435            return f"TTL {self.expressions(expression, flat=True)}"
3436
3437        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
3438
3439    def transaction_sql(self, expression: exp.Transaction) -> str:
3440        return "BEGIN"
3441
3442    def commit_sql(self, expression: exp.Commit) -> str:
3443        chain = expression.args.get("chain")
3444        if chain is not None:
3445            chain = " AND CHAIN" if chain else " AND NO CHAIN"
3446
3447        return f"COMMIT{chain or ''}"
3448
3449    def rollback_sql(self, expression: exp.Rollback) -> str:
3450        savepoint = expression.args.get("savepoint")
3451        savepoint = f" TO {savepoint}" if savepoint else ""
3452        return f"ROLLBACK{savepoint}"
3453
3454    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
3455        this = self.sql(expression, "this")
3456
3457        dtype = self.sql(expression, "dtype")
3458        if dtype:
3459            collate = self.sql(expression, "collate")
3460            collate = f" COLLATE {collate}" if collate else ""
3461            using = self.sql(expression, "using")
3462            using = f" USING {using}" if using else ""
3463            alter_set_type = self.ALTER_SET_TYPE + " " if self.ALTER_SET_TYPE else ""
3464            return f"ALTER COLUMN {this} {alter_set_type}{dtype}{collate}{using}"
3465
3466        default = self.sql(expression, "default")
3467        if default:
3468            return f"ALTER COLUMN {this} SET DEFAULT {default}"
3469
3470        comment = self.sql(expression, "comment")
3471        if comment:
3472            return f"ALTER COLUMN {this} COMMENT {comment}"
3473
3474        visible = expression.args.get("visible")
3475        if visible:
3476            return f"ALTER COLUMN {this} SET {visible}"
3477
3478        allow_null = expression.args.get("allow_null")
3479        drop = expression.args.get("drop")
3480
3481        if not drop and not allow_null:
3482            self.unsupported("Unsupported ALTER COLUMN syntax")
3483
3484        if allow_null is not None:
3485            keyword = "DROP" if drop else "SET"
3486            return f"ALTER COLUMN {this} {keyword} NOT NULL"
3487
3488        return f"ALTER COLUMN {this} DROP DEFAULT"
3489
3490    def alterindex_sql(self, expression: exp.AlterIndex) -> str:
3491        this = self.sql(expression, "this")
3492
3493        visible = expression.args.get("visible")
3494        visible_sql = "VISIBLE" if visible else "INVISIBLE"
3495
3496        return f"ALTER INDEX {this} {visible_sql}"
3497
3498    def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
3499        this = self.sql(expression, "this")
3500        if not isinstance(expression.this, exp.Var):
3501            this = f"KEY DISTKEY {this}"
3502        return f"ALTER DISTSTYLE {this}"
3503
3504    def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
3505        compound = " COMPOUND" if expression.args.get("compound") else ""
3506        this = self.sql(expression, "this")
3507        expressions = self.expressions(expression, flat=True)
3508        expressions = f"({expressions})" if expressions else ""
3509        return f"ALTER{compound} SORTKEY {this or expressions}"
3510
3511    def alterrename_sql(self, expression: exp.AlterRename, include_to: bool = True) -> str:
3512        if not self.RENAME_TABLE_WITH_DB:
3513            # Remove db from tables
3514            expression = expression.transform(
3515                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
3516            ).assert_is(exp.AlterRename)
3517        this = self.sql(expression, "this")
3518        to_kw = " TO" if include_to else ""
3519        return f"RENAME{to_kw} {this}"
3520
3521    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
3522        exists = " IF EXISTS" if expression.args.get("exists") else ""
3523        old_column = self.sql(expression, "this")
3524        new_column = self.sql(expression, "to")
3525        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
3526
3527    def alterset_sql(self, expression: exp.AlterSet) -> str:
3528        exprs = self.expressions(expression, flat=True)
3529        if self.ALTER_SET_WRAPPED:
3530            exprs = f"({exprs})"
3531
3532        return f"SET {exprs}"
3533
3534    def alter_sql(self, expression: exp.Alter) -> str:
3535        actions = expression.args["actions"]
3536
3537        if not self.dialect.ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN and isinstance(
3538            actions[0], exp.ColumnDef
3539        ):
3540            actions_sql = self.expressions(expression, key="actions", flat=True)
3541            actions_sql = f"ADD {actions_sql}"
3542        else:
3543            actions_list = []
3544            for action in actions:
3545                if isinstance(action, (exp.ColumnDef, exp.Schema)):
3546                    action_sql = self.add_column_sql(action)
3547                else:
3548                    action_sql = self.sql(action)
3549                    if isinstance(action, exp.Query):
3550                        action_sql = f"AS {action_sql}"
3551
3552                actions_list.append(action_sql)
3553
3554            actions_sql = self.format_args(*actions_list).lstrip("\n")
3555
3556        exists = " IF EXISTS" if expression.args.get("exists") else ""
3557        on_cluster = self.sql(expression, "cluster")
3558        on_cluster = f" {on_cluster}" if on_cluster else ""
3559        only = " ONLY" if expression.args.get("only") else ""
3560        options = self.expressions(expression, key="options")
3561        options = f", {options}" if options else ""
3562        kind = self.sql(expression, "kind")
3563        not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
3564        check = " WITH CHECK" if expression.args.get("check") else ""
3565
3566        return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster}{check}{self.sep()}{actions_sql}{not_valid}{options}"
3567
3568    def add_column_sql(self, expression: exp.Expression) -> str:
3569        sql = self.sql(expression)
3570        if isinstance(expression, exp.Schema):
3571            column_text = " COLUMNS"
3572        elif isinstance(expression, exp.ColumnDef) and self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
3573            column_text = " COLUMN"
3574        else:
3575            column_text = ""
3576
3577        return f"ADD{column_text} {sql}"
3578
3579    def droppartition_sql(self, expression: exp.DropPartition) -> str:
3580        expressions = self.expressions(expression)
3581        exists = " IF EXISTS " if expression.args.get("exists") else " "
3582        return f"DROP{exists}{expressions}"
3583
3584    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
3585        return f"ADD {self.expressions(expression, indent=False)}"
3586
3587    def addpartition_sql(self, expression: exp.AddPartition) -> str:
3588        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
3589        location = self.sql(expression, "location")
3590        location = f" {location}" if location else ""
3591        return f"ADD {exists}{self.sql(expression.this)}{location}"
3592
3593    def distinct_sql(self, expression: exp.Distinct) -> str:
3594        this = self.expressions(expression, flat=True)
3595
3596        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
3597            case = exp.case()
3598            for arg in expression.expressions:
3599                case = case.when(arg.is_(exp.null()), exp.null())
3600            this = self.sql(case.else_(f"({this})"))
3601
3602        this = f" {this}" if this else ""
3603
3604        on = self.sql(expression, "on")
3605        on = f" ON {on}" if on else ""
3606        return f"DISTINCT{this}{on}"
3607
3608    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
3609        return self._embed_ignore_nulls(expression, "IGNORE NULLS")
3610
3611    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
3612        return self._embed_ignore_nulls(expression, "RESPECT NULLS")
3613
3614    def havingmax_sql(self, expression: exp.HavingMax) -> str:
3615        this_sql = self.sql(expression, "this")
3616        expression_sql = self.sql(expression, "expression")
3617        kind = "MAX" if expression.args.get("max") else "MIN"
3618        return f"{this_sql} HAVING {kind} {expression_sql}"
3619
3620    def intdiv_sql(self, expression: exp.IntDiv) -> str:
3621        return self.sql(
3622            exp.Cast(
3623                this=exp.Div(this=expression.this, expression=expression.expression),
3624                to=exp.DataType(this=exp.DataType.Type.INT),
3625            )
3626        )
3627
3628    def dpipe_sql(self, expression: exp.DPipe) -> str:
3629        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
3630            return self.func(
3631                "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten())
3632            )
3633        return self.binary(expression, "||")
3634
3635    def div_sql(self, expression: exp.Div) -> str:
3636        l, r = expression.left, expression.right
3637
3638        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
3639            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
3640
3641        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
3642            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
3643                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
3644
3645        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
3646            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
3647                return self.sql(
3648                    exp.cast(
3649                        l / r,
3650                        to=exp.DataType.Type.BIGINT,
3651                    )
3652                )
3653
3654        return self.binary(expression, "/")
3655
3656    def safedivide_sql(self, expression: exp.SafeDivide) -> str:
3657        n = exp._wrap(expression.this, exp.Binary)
3658        d = exp._wrap(expression.expression, exp.Binary)
3659        return self.sql(exp.If(this=d.neq(0), true=n / d, false=exp.Null()))
3660
3661    def overlaps_sql(self, expression: exp.Overlaps) -> str:
3662        return self.binary(expression, "OVERLAPS")
3663
3664    def distance_sql(self, expression: exp.Distance) -> str:
3665        return self.binary(expression, "<->")
3666
3667    def dot_sql(self, expression: exp.Dot) -> str:
3668        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
3669
3670    def eq_sql(self, expression: exp.EQ) -> str:
3671        return self.binary(expression, "=")
3672
3673    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
3674        return self.binary(expression, ":=")
3675
3676    def escape_sql(self, expression: exp.Escape) -> str:
3677        return self.binary(expression, "ESCAPE")
3678
3679    def glob_sql(self, expression: exp.Glob) -> str:
3680        return self.binary(expression, "GLOB")
3681
3682    def gt_sql(self, expression: exp.GT) -> str:
3683        return self.binary(expression, ">")
3684
3685    def gte_sql(self, expression: exp.GTE) -> str:
3686        return self.binary(expression, ">=")
3687
3688    def is_sql(self, expression: exp.Is) -> str:
3689        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
3690            return self.sql(
3691                expression.this if expression.expression.this else exp.not_(expression.this)
3692            )
3693        return self.binary(expression, "IS")
3694
3695    def _like_sql(self, expression: exp.Like | exp.ILike) -> str:
3696        this = expression.this
3697        rhs = expression.expression
3698
3699        if isinstance(expression, exp.Like):
3700            exp_class: t.Type[exp.Like | exp.ILike] = exp.Like
3701            op = "LIKE"
3702        else:
3703            exp_class = exp.ILike
3704            op = "ILIKE"
3705
3706        if isinstance(rhs, (exp.All, exp.Any)) and not self.SUPPORTS_LIKE_QUANTIFIERS:
3707            exprs = rhs.this.unnest()
3708
3709            if isinstance(exprs, exp.Tuple):
3710                exprs = exprs.expressions
3711
3712            connective = exp.or_ if isinstance(rhs, exp.Any) else exp.and_
3713
3714            like_expr: exp.Expression = exp_class(this=this, expression=exprs[0])
3715            for expr in exprs[1:]:
3716                like_expr = connective(like_expr, exp_class(this=this, expression=expr))
3717
3718            return self.sql(like_expr)
3719
3720        return self.binary(expression, op)
3721
3722    def like_sql(self, expression: exp.Like) -> str:
3723        return self._like_sql(expression)
3724
3725    def ilike_sql(self, expression: exp.ILike) -> str:
3726        return self._like_sql(expression)
3727
3728    def similarto_sql(self, expression: exp.SimilarTo) -> str:
3729        return self.binary(expression, "SIMILAR TO")
3730
3731    def lt_sql(self, expression: exp.LT) -> str:
3732        return self.binary(expression, "<")
3733
3734    def lte_sql(self, expression: exp.LTE) -> str:
3735        return self.binary(expression, "<=")
3736
3737    def mod_sql(self, expression: exp.Mod) -> str:
3738        return self.binary(expression, "%")
3739
3740    def mul_sql(self, expression: exp.Mul) -> str:
3741        return self.binary(expression, "*")
3742
3743    def neq_sql(self, expression: exp.NEQ) -> str:
3744        return self.binary(expression, "<>")
3745
3746    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
3747        return self.binary(expression, "IS NOT DISTINCT FROM")
3748
3749    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
3750        return self.binary(expression, "IS DISTINCT FROM")
3751
3752    def slice_sql(self, expression: exp.Slice) -> str:
3753        return self.binary(expression, ":")
3754
3755    def sub_sql(self, expression: exp.Sub) -> str:
3756        return self.binary(expression, "-")
3757
3758    def trycast_sql(self, expression: exp.TryCast) -> str:
3759        return self.cast_sql(expression, safe_prefix="TRY_")
3760
3761    def jsoncast_sql(self, expression: exp.JSONCast) -> str:
3762        return self.cast_sql(expression)
3763
3764    def try_sql(self, expression: exp.Try) -> str:
3765        if not self.TRY_SUPPORTED:
3766            self.unsupported("Unsupported TRY function")
3767            return self.sql(expression, "this")
3768
3769        return self.func("TRY", expression.this)
3770
3771    def log_sql(self, expression: exp.Log) -> str:
3772        this = expression.this
3773        expr = expression.expression
3774
3775        if self.dialect.LOG_BASE_FIRST is False:
3776            this, expr = expr, this
3777        elif self.dialect.LOG_BASE_FIRST is None and expr:
3778            if this.name in ("2", "10"):
3779                return self.func(f"LOG{this.name}", expr)
3780
3781            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
3782
3783        return self.func("LOG", this, expr)
3784
3785    def use_sql(self, expression: exp.Use) -> str:
3786        kind = self.sql(expression, "kind")
3787        kind = f" {kind}" if kind else ""
3788        this = self.sql(expression, "this") or self.expressions(expression, flat=True)
3789        this = f" {this}" if this else ""
3790        return f"USE{kind}{this}"
3791
3792    def binary(self, expression: exp.Binary, op: str) -> str:
3793        sqls: t.List[str] = []
3794        stack: t.List[t.Union[str, exp.Expression]] = [expression]
3795        binary_type = type(expression)
3796
3797        while stack:
3798            node = stack.pop()
3799
3800            if type(node) is binary_type:
3801                op_func = node.args.get("operator")
3802                if op_func:
3803                    op = f"OPERATOR({self.sql(op_func)})"
3804
3805                stack.append(node.right)
3806                stack.append(f" {self.maybe_comment(op, comments=node.comments)} ")
3807                stack.append(node.left)
3808            else:
3809                sqls.append(self.sql(node))
3810
3811        return "".join(sqls)
3812
3813    def ceil_floor(self, expression: exp.Ceil | exp.Floor) -> str:
3814        to_clause = self.sql(expression, "to")
3815        if to_clause:
3816            return f"{expression.sql_name()}({self.sql(expression, 'this')} TO {to_clause})"
3817
3818        return self.function_fallback_sql(expression)
3819
3820    def function_fallback_sql(self, expression: exp.Func) -> str:
3821        args = []
3822
3823        for key in expression.arg_types:
3824            arg_value = expression.args.get(key)
3825
3826            if isinstance(arg_value, list):
3827                for value in arg_value:
3828                    args.append(value)
3829            elif arg_value is not None:
3830                args.append(arg_value)
3831
3832        if self.dialect.PRESERVE_ORIGINAL_NAMES:
3833            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
3834        else:
3835            name = expression.sql_name()
3836
3837        return self.func(name, *args)
3838
3839    def func(
3840        self,
3841        name: str,
3842        *args: t.Optional[exp.Expression | str],
3843        prefix: str = "(",
3844        suffix: str = ")",
3845        normalize: bool = True,
3846    ) -> str:
3847        name = self.normalize_func(name) if normalize else name
3848        return f"{name}{prefix}{self.format_args(*args)}{suffix}"
3849
3850    def format_args(self, *args: t.Optional[str | exp.Expression], sep: str = ", ") -> str:
3851        arg_sqls = tuple(
3852            self.sql(arg) for arg in args if arg is not None and not isinstance(arg, bool)
3853        )
3854        if self.pretty and self.too_wide(arg_sqls):
3855            return self.indent(
3856                "\n" + f"{sep.strip()}\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True
3857            )
3858        return sep.join(arg_sqls)
3859
3860    def too_wide(self, args: t.Iterable) -> bool:
3861        return sum(len(arg) for arg in args) > self.max_text_width
3862
3863    def format_time(
3864        self,
3865        expression: exp.Expression,
3866        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
3867        inverse_time_trie: t.Optional[t.Dict] = None,
3868    ) -> t.Optional[str]:
3869        return format_time(
3870            self.sql(expression, "format"),
3871            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
3872            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
3873        )
3874
3875    def expressions(
3876        self,
3877        expression: t.Optional[exp.Expression] = None,
3878        key: t.Optional[str] = None,
3879        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
3880        flat: bool = False,
3881        indent: bool = True,
3882        skip_first: bool = False,
3883        skip_last: bool = False,
3884        sep: str = ", ",
3885        prefix: str = "",
3886        dynamic: bool = False,
3887        new_line: bool = False,
3888    ) -> str:
3889        expressions = expression.args.get(key or "expressions") if expression else sqls
3890
3891        if not expressions:
3892            return ""
3893
3894        if flat:
3895            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
3896
3897        num_sqls = len(expressions)
3898        result_sqls = []
3899
3900        for i, e in enumerate(expressions):
3901            sql = self.sql(e, comment=False)
3902            if not sql:
3903                continue
3904
3905            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
3906
3907            if self.pretty:
3908                if self.leading_comma:
3909                    result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}")
3910                else:
3911                    result_sqls.append(
3912                        f"{prefix}{sql}{(sep.rstrip() if comments else sep) if i + 1 < num_sqls else ''}{comments}"
3913                    )
3914            else:
3915                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
3916
3917        if self.pretty and (not dynamic or self.too_wide(result_sqls)):
3918            if new_line:
3919                result_sqls.insert(0, "")
3920                result_sqls.append("")
3921            result_sql = "\n".join(s.rstrip() for s in result_sqls)
3922        else:
3923            result_sql = "".join(result_sqls)
3924
3925        return (
3926            self.indent(result_sql, skip_first=skip_first, skip_last=skip_last)
3927            if indent
3928            else result_sql
3929        )
3930
3931    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
3932        flat = flat or isinstance(expression.parent, exp.Properties)
3933        expressions_sql = self.expressions(expression, flat=flat)
3934        if flat:
3935            return f"{op} {expressions_sql}"
3936        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
3937
3938    def naked_property(self, expression: exp.Property) -> str:
3939        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
3940        if not property_name:
3941            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
3942        return f"{property_name} {self.sql(expression, 'this')}"
3943
3944    def tag_sql(self, expression: exp.Tag) -> str:
3945        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
3946
3947    def token_sql(self, token_type: TokenType) -> str:
3948        return self.TOKEN_MAPPING.get(token_type, token_type.name)
3949
3950    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
3951        this = self.sql(expression, "this")
3952        expressions = self.no_identify(self.expressions, expression)
3953        expressions = (
3954            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
3955        )
3956        return f"{this}{expressions}" if expressions.strip() != "" else this
3957
3958    def joinhint_sql(self, expression: exp.JoinHint) -> str:
3959        this = self.sql(expression, "this")
3960        expressions = self.expressions(expression, flat=True)
3961        return f"{this}({expressions})"
3962
3963    def kwarg_sql(self, expression: exp.Kwarg) -> str:
3964        return self.binary(expression, "=>")
3965
3966    def when_sql(self, expression: exp.When) -> str:
3967        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
3968        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
3969        condition = self.sql(expression, "condition")
3970        condition = f" AND {condition}" if condition else ""
3971
3972        then_expression = expression.args.get("then")
3973        if isinstance(then_expression, exp.Insert):
3974            this = self.sql(then_expression, "this")
3975            this = f"INSERT {this}" if this else "INSERT"
3976            then = self.sql(then_expression, "expression")
3977            then = f"{this} VALUES {then}" if then else this
3978        elif isinstance(then_expression, exp.Update):
3979            if isinstance(then_expression.args.get("expressions"), exp.Star):
3980                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
3981            else:
3982                then = f"UPDATE SET{self.sep()}{self.expressions(then_expression)}"
3983        else:
3984            then = self.sql(then_expression)
3985        return f"WHEN {matched}{source}{condition} THEN {then}"
3986
3987    def whens_sql(self, expression: exp.Whens) -> str:
3988        return self.expressions(expression, sep=" ", indent=False)
3989
3990    def merge_sql(self, expression: exp.Merge) -> str:
3991        table = expression.this
3992        table_alias = ""
3993
3994        hints = table.args.get("hints")
3995        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
3996            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
3997            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
3998
3999        this = self.sql(table)
4000        using = f"USING {self.sql(expression, 'using')}"
4001        on = f"ON {self.sql(expression, 'on')}"
4002        whens = self.sql(expression, "whens")
4003
4004        returning = self.sql(expression, "returning")
4005        if returning:
4006            whens = f"{whens}{returning}"
4007
4008        sep = self.sep()
4009
4010        return self.prepend_ctes(
4011            expression,
4012            f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{whens}",
4013        )
4014
4015    @unsupported_args("format")
4016    def tochar_sql(self, expression: exp.ToChar) -> str:
4017        return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT))
4018
4019    def tonumber_sql(self, expression: exp.ToNumber) -> str:
4020        if not self.SUPPORTS_TO_NUMBER:
4021            self.unsupported("Unsupported TO_NUMBER function")
4022            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
4023
4024        fmt = expression.args.get("format")
4025        if not fmt:
4026            self.unsupported("Conversion format is required for TO_NUMBER")
4027            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
4028
4029        return self.func("TO_NUMBER", expression.this, fmt)
4030
4031    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
4032        this = self.sql(expression, "this")
4033        kind = self.sql(expression, "kind")
4034        settings_sql = self.expressions(expression, key="settings", sep=" ")
4035        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
4036        return f"{this}({kind}{args})"
4037
4038    def dictrange_sql(self, expression: exp.DictRange) -> str:
4039        this = self.sql(expression, "this")
4040        max = self.sql(expression, "max")
4041        min = self.sql(expression, "min")
4042        return f"{this}(MIN {min} MAX {max})"
4043
4044    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
4045        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
4046
4047    def duplicatekeyproperty_sql(self, expression: exp.DuplicateKeyProperty) -> str:
4048        return f"DUPLICATE KEY ({self.expressions(expression, flat=True)})"
4049
4050    # https://docs.starrocks.io/docs/sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE/
4051    def uniquekeyproperty_sql(
4052        self, expression: exp.UniqueKeyProperty, prefix: str = "UNIQUE KEY"
4053    ) -> str:
4054        return f"{prefix} ({self.expressions(expression, flat=True)})"
4055
4056    # https://docs.starrocks.io/docs/sql-reference/sql-statements/data-definition/CREATE_TABLE/#distribution_desc
4057    def distributedbyproperty_sql(self, expression: exp.DistributedByProperty) -> str:
4058        expressions = self.expressions(expression, flat=True)
4059        expressions = f" {self.wrap(expressions)}" if expressions else ""
4060        buckets = self.sql(expression, "buckets")
4061        kind = self.sql(expression, "kind")
4062        buckets = f" BUCKETS {buckets}" if buckets else ""
4063        order = self.sql(expression, "order")
4064        return f"DISTRIBUTED BY {kind}{expressions}{buckets}{order}"
4065
4066    def oncluster_sql(self, expression: exp.OnCluster) -> str:
4067        return ""
4068
4069    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
4070        expressions = self.expressions(expression, key="expressions", flat=True)
4071        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
4072        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
4073        buckets = self.sql(expression, "buckets")
4074        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
4075
4076    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
4077        this = self.sql(expression, "this")
4078        having = self.sql(expression, "having")
4079
4080        if having:
4081            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
4082
4083        return self.func("ANY_VALUE", this)
4084
4085    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
4086        transform = self.func("TRANSFORM", *expression.expressions)
4087        row_format_before = self.sql(expression, "row_format_before")
4088        row_format_before = f" {row_format_before}" if row_format_before else ""
4089        record_writer = self.sql(expression, "record_writer")
4090        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
4091        using = f" USING {self.sql(expression, 'command_script')}"
4092        schema = self.sql(expression, "schema")
4093        schema = f" AS {schema}" if schema else ""
4094        row_format_after = self.sql(expression, "row_format_after")
4095        row_format_after = f" {row_format_after}" if row_format_after else ""
4096        record_reader = self.sql(expression, "record_reader")
4097        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
4098        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
4099
4100    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
4101        key_block_size = self.sql(expression, "key_block_size")
4102        if key_block_size:
4103            return f"KEY_BLOCK_SIZE = {key_block_size}"
4104
4105        using = self.sql(expression, "using")
4106        if using:
4107            return f"USING {using}"
4108
4109        parser = self.sql(expression, "parser")
4110        if parser:
4111            return f"WITH PARSER {parser}"
4112
4113        comment = self.sql(expression, "comment")
4114        if comment:
4115            return f"COMMENT {comment}"
4116
4117        visible = expression.args.get("visible")
4118        if visible is not None:
4119            return "VISIBLE" if visible else "INVISIBLE"
4120
4121        engine_attr = self.sql(expression, "engine_attr")
4122        if engine_attr:
4123            return f"ENGINE_ATTRIBUTE = {engine_attr}"
4124
4125        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
4126        if secondary_engine_attr:
4127            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
4128
4129        self.unsupported("Unsupported index constraint option.")
4130        return ""
4131
4132    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
4133        enforced = " ENFORCED" if expression.args.get("enforced") else ""
4134        return f"CHECK ({self.sql(expression, 'this')}){enforced}"
4135
4136    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
4137        kind = self.sql(expression, "kind")
4138        kind = f"{kind} INDEX" if kind else "INDEX"
4139        this = self.sql(expression, "this")
4140        this = f" {this}" if this else ""
4141        index_type = self.sql(expression, "index_type")
4142        index_type = f" USING {index_type}" if index_type else ""
4143        expressions = self.expressions(expression, flat=True)
4144        expressions = f" ({expressions})" if expressions else ""
4145        options = self.expressions(expression, key="options", sep=" ")
4146        options = f" {options}" if options else ""
4147        return f"{kind}{this}{index_type}{expressions}{options}"
4148
4149    def nvl2_sql(self, expression: exp.Nvl2) -> str:
4150        if self.NVL2_SUPPORTED:
4151            return self.function_fallback_sql(expression)
4152
4153        case = exp.Case().when(
4154            expression.this.is_(exp.null()).not_(copy=False),
4155            expression.args["true"],
4156            copy=False,
4157        )
4158        else_cond = expression.args.get("false")
4159        if else_cond:
4160            case.else_(else_cond, copy=False)
4161
4162        return self.sql(case)
4163
4164    def comprehension_sql(self, expression: exp.Comprehension) -> str:
4165        this = self.sql(expression, "this")
4166        expr = self.sql(expression, "expression")
4167        iterator = self.sql(expression, "iterator")
4168        condition = self.sql(expression, "condition")
4169        condition = f" IF {condition}" if condition else ""
4170        return f"{this} FOR {expr} IN {iterator}{condition}"
4171
4172    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
4173        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
4174
4175    def opclass_sql(self, expression: exp.Opclass) -> str:
4176        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
4177
4178    def predict_sql(self, expression: exp.Predict) -> str:
4179        model = self.sql(expression, "this")
4180        model = f"MODEL {model}"
4181        table = self.sql(expression, "expression")
4182        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
4183        parameters = self.sql(expression, "params_struct")
4184        return self.func("PREDICT", model, table, parameters or None)
4185
4186    def generateembedding_sql(self, expression: exp.GenerateEmbedding) -> str:
4187        model = self.sql(expression, "this")
4188        model = f"MODEL {model}"
4189        table = self.sql(expression, "expression")
4190        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
4191        parameters = self.sql(expression, "params_struct")
4192        return self.func("GENERATE_EMBEDDING", model, table, parameters or None)
4193
4194    def featuresattime_sql(self, expression: exp.FeaturesAtTime) -> str:
4195        this_sql = self.sql(expression, "this")
4196        if isinstance(expression.this, exp.Table):
4197            this_sql = f"TABLE {this_sql}"
4198
4199        return self.func(
4200            "FEATURES_AT_TIME",
4201            this_sql,
4202            expression.args.get("time"),
4203            expression.args.get("num_rows"),
4204            expression.args.get("ignore_feature_nulls"),
4205        )
4206
4207    def vectorsearch_sql(self, expression: exp.VectorSearch) -> str:
4208        this_sql = self.sql(expression, "this")
4209        if isinstance(expression.this, exp.Table):
4210            this_sql = f"TABLE {this_sql}"
4211
4212        query_table = self.sql(expression, "query_table")
4213        if isinstance(expression.args["query_table"], exp.Table):
4214            query_table = f"TABLE {query_table}"
4215
4216        return self.func(
4217            "VECTOR_SEARCH",
4218            this_sql,
4219            expression.args.get("column_to_search"),
4220            query_table,
4221            expression.args.get("query_column_to_search"),
4222            expression.args.get("top_k"),
4223            expression.args.get("distance_type"),
4224            expression.args.get("options"),
4225        )
4226
4227    def forin_sql(self, expression: exp.ForIn) -> str:
4228        this = self.sql(expression, "this")
4229        expression_sql = self.sql(expression, "expression")
4230        return f"FOR {this} DO {expression_sql}"
4231
4232    def refresh_sql(self, expression: exp.Refresh) -> str:
4233        this = self.sql(expression, "this")
4234        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
4235        return f"REFRESH {table}{this}"
4236
4237    def toarray_sql(self, expression: exp.ToArray) -> str:
4238        arg = expression.this
4239        if not arg.type:
4240            from sqlglot.optimizer.annotate_types import annotate_types
4241
4242            arg = annotate_types(arg, dialect=self.dialect)
4243
4244        if arg.is_type(exp.DataType.Type.ARRAY):
4245            return self.sql(arg)
4246
4247        cond_for_null = arg.is_(exp.null())
4248        return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
4249
4250    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
4251        this = expression.this
4252        time_format = self.format_time(expression)
4253
4254        if time_format:
4255            return self.sql(
4256                exp.cast(
4257                    exp.StrToTime(this=this, format=expression.args["format"]),
4258                    exp.DataType.Type.TIME,
4259                )
4260            )
4261
4262        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
4263            return self.sql(this)
4264
4265        return self.sql(exp.cast(this, exp.DataType.Type.TIME))
4266
4267    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
4268        this = expression.this
4269        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
4270            return self.sql(this)
4271
4272        return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP, dialect=self.dialect))
4273
4274    def tsordstodatetime_sql(self, expression: exp.TsOrDsToDatetime) -> str:
4275        this = expression.this
4276        if isinstance(this, exp.TsOrDsToDatetime) or this.is_type(exp.DataType.Type.DATETIME):
4277            return self.sql(this)
4278
4279        return self.sql(exp.cast(this, exp.DataType.Type.DATETIME, dialect=self.dialect))
4280
4281    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
4282        this = expression.this
4283        time_format = self.format_time(expression)
4284
4285        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
4286            return self.sql(
4287                exp.cast(
4288                    exp.StrToTime(this=this, format=expression.args["format"]),
4289                    exp.DataType.Type.DATE,
4290                )
4291            )
4292
4293        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
4294            return self.sql(this)
4295
4296        return self.sql(exp.cast(this, exp.DataType.Type.DATE))
4297
4298    def unixdate_sql(self, expression: exp.UnixDate) -> str:
4299        return self.sql(
4300            exp.func(
4301                "DATEDIFF",
4302                expression.this,
4303                exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
4304                "day",
4305            )
4306        )
4307
4308    def lastday_sql(self, expression: exp.LastDay) -> str:
4309        if self.LAST_DAY_SUPPORTS_DATE_PART:
4310            return self.function_fallback_sql(expression)
4311
4312        unit = expression.text("unit")
4313        if unit and unit != "MONTH":
4314            self.unsupported("Date parts are not supported in LAST_DAY.")
4315
4316        return self.func("LAST_DAY", expression.this)
4317
4318    def dateadd_sql(self, expression: exp.DateAdd) -> str:
4319        from sqlglot.dialects.dialect import unit_to_str
4320
4321        return self.func(
4322            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
4323        )
4324
4325    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
4326        if self.CAN_IMPLEMENT_ARRAY_ANY:
4327            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
4328            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
4329            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
4330            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
4331
4332        from sqlglot.dialects import Dialect
4333
4334        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
4335        if self.dialect.__class__ != Dialect:
4336            self.unsupported("ARRAY_ANY is unsupported")
4337
4338        return self.function_fallback_sql(expression)
4339
4340    def struct_sql(self, expression: exp.Struct) -> str:
4341        expression.set(
4342            "expressions",
4343            [
4344                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
4345                if isinstance(e, exp.PropertyEQ)
4346                else e
4347                for e in expression.expressions
4348            ],
4349        )
4350
4351        return self.function_fallback_sql(expression)
4352
4353    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
4354        low = self.sql(expression, "this")
4355        high = self.sql(expression, "expression")
4356
4357        return f"{low} TO {high}"
4358
4359    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
4360        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
4361        tables = f" {self.expressions(expression)}"
4362
4363        exists = " IF EXISTS" if expression.args.get("exists") else ""
4364
4365        on_cluster = self.sql(expression, "cluster")
4366        on_cluster = f" {on_cluster}" if on_cluster else ""
4367
4368        identity = self.sql(expression, "identity")
4369        identity = f" {identity} IDENTITY" if identity else ""
4370
4371        option = self.sql(expression, "option")
4372        option = f" {option}" if option else ""
4373
4374        partition = self.sql(expression, "partition")
4375        partition = f" {partition}" if partition else ""
4376
4377        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
4378
4379    # This transpiles T-SQL's CONVERT function
4380    # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16
4381    def convert_sql(self, expression: exp.Convert) -> str:
4382        to = expression.this
4383        value = expression.expression
4384        style = expression.args.get("style")
4385        safe = expression.args.get("safe")
4386        strict = expression.args.get("strict")
4387
4388        if not to or not value:
4389            return ""
4390
4391        # Retrieve length of datatype and override to default if not specified
4392        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
4393            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
4394
4395        transformed: t.Optional[exp.Expression] = None
4396        cast = exp.Cast if strict else exp.TryCast
4397
4398        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
4399        if isinstance(style, exp.Literal) and style.is_int:
4400            from sqlglot.dialects.tsql import TSQL
4401
4402            style_value = style.name
4403            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
4404            if not converted_style:
4405                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
4406
4407            fmt = exp.Literal.string(converted_style)
4408
4409            if to.this == exp.DataType.Type.DATE:
4410                transformed = exp.StrToDate(this=value, format=fmt)
4411            elif to.this in (exp.DataType.Type.DATETIME, exp.DataType.Type.DATETIME2):
4412                transformed = exp.StrToTime(this=value, format=fmt)
4413            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
4414                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
4415            elif to.this == exp.DataType.Type.TEXT:
4416                transformed = exp.TimeToStr(this=value, format=fmt)
4417
4418        if not transformed:
4419            transformed = cast(this=value, to=to, safe=safe)
4420
4421        return self.sql(transformed)
4422
4423    def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str:
4424        this = expression.this
4425        if isinstance(this, exp.JSONPathWildcard):
4426            this = self.json_path_part(this)
4427            return f".{this}" if this else ""
4428
4429        if self.SAFE_JSON_PATH_KEY_RE.match(this):
4430            return f".{this}"
4431
4432        this = self.json_path_part(this)
4433        return (
4434            f"[{this}]"
4435            if self._quote_json_path_key_using_brackets and self.JSON_PATH_BRACKETED_KEY_SUPPORTED
4436            else f".{this}"
4437        )
4438
4439    def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str:
4440        this = self.json_path_part(expression.this)
4441        return f"[{this}]" if this else ""
4442
4443    def _simplify_unless_literal(self, expression: E) -> E:
4444        if not isinstance(expression, exp.Literal):
4445            from sqlglot.optimizer.simplify import simplify
4446
4447            expression = simplify(expression, dialect=self.dialect)
4448
4449        return expression
4450
4451    def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str:
4452        this = expression.this
4453        if isinstance(this, self.RESPECT_IGNORE_NULLS_UNSUPPORTED_EXPRESSIONS):
4454            self.unsupported(
4455                f"RESPECT/IGNORE NULLS is not supported for {type(this).key} in {self.dialect.__class__.__name__}"
4456            )
4457            return self.sql(this)
4458
4459        if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"):
4460            # The first modifier here will be the one closest to the AggFunc's arg
4461            mods = sorted(
4462                expression.find_all(exp.HavingMax, exp.Order, exp.Limit),
4463                key=lambda x: 0
4464                if isinstance(x, exp.HavingMax)
4465                else (1 if isinstance(x, exp.Order) else 2),
4466            )
4467
4468            if mods:
4469                mod = mods[0]
4470                this = expression.__class__(this=mod.this.copy())
4471                this.meta["inline"] = True
4472                mod.this.replace(this)
4473                return self.sql(expression.this)
4474
4475            agg_func = expression.find(exp.AggFunc)
4476
4477            if agg_func:
4478                agg_func_sql = self.sql(agg_func, comment=False)[:-1] + f" {text})"
4479                return self.maybe_comment(agg_func_sql, comments=agg_func.comments)
4480
4481        return f"{self.sql(expression, 'this')} {text}"
4482
4483    def _replace_line_breaks(self, string: str) -> str:
4484        """We don't want to extra indent line breaks so we temporarily replace them with sentinels."""
4485        if self.pretty:
4486            return string.replace("\n", self.SENTINEL_LINE_BREAK)
4487        return string
4488
4489    def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
4490        option = self.sql(expression, "this")
4491
4492        if expression.expressions:
4493            upper = option.upper()
4494
4495            # Snowflake FILE_FORMAT options are separated by whitespace
4496            sep = " " if upper == "FILE_FORMAT" else ", "
4497
4498            # Databricks copy/format options do not set their list of values with EQ
4499            op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = "
4500            values = self.expressions(expression, flat=True, sep=sep)
4501            return f"{option}{op}({values})"
4502
4503        value = self.sql(expression, "expression")
4504
4505        if not value:
4506            return option
4507
4508        op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " "
4509
4510        return f"{option}{op}{value}"
4511
4512    def credentials_sql(self, expression: exp.Credentials) -> str:
4513        cred_expr = expression.args.get("credentials")
4514        if isinstance(cred_expr, exp.Literal):
4515            # Redshift case: CREDENTIALS <string>
4516            credentials = self.sql(expression, "credentials")
4517            credentials = f"CREDENTIALS {credentials}" if credentials else ""
4518        else:
4519            # Snowflake case: CREDENTIALS = (...)
4520            credentials = self.expressions(expression, key="credentials", flat=True, sep=" ")
4521            credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else ""
4522
4523        storage = self.sql(expression, "storage")
4524        storage = f"STORAGE_INTEGRATION = {storage}" if storage else ""
4525
4526        encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
4527        encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
4528
4529        iam_role = self.sql(expression, "iam_role")
4530        iam_role = f"IAM_ROLE {iam_role}" if iam_role else ""
4531
4532        region = self.sql(expression, "region")
4533        region = f" REGION {region}" if region else ""
4534
4535        return f"{credentials}{storage}{encryption}{iam_role}{region}"
4536
4537    def copy_sql(self, expression: exp.Copy) -> str:
4538        this = self.sql(expression, "this")
4539        this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
4540
4541        credentials = self.sql(expression, "credentials")
4542        credentials = self.seg(credentials) if credentials else ""
4543        kind = self.seg("FROM" if expression.args.get("kind") else "TO")
4544        files = self.expressions(expression, key="files", flat=True)
4545
4546        sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
4547        params = self.expressions(
4548            expression,
4549            key="params",
4550            sep=sep,
4551            new_line=True,
4552            skip_last=True,
4553            skip_first=True,
4554            indent=self.COPY_PARAMS_ARE_WRAPPED,
4555        )
4556
4557        if params:
4558            if self.COPY_PARAMS_ARE_WRAPPED:
4559                params = f" WITH ({params})"
4560            elif not self.pretty:
4561                params = f" {params}"
4562
4563        return f"COPY{this}{kind} {files}{credentials}{params}"
4564
4565    def semicolon_sql(self, expression: exp.Semicolon) -> str:
4566        return ""
4567
4568    def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
4569        on_sql = "ON" if expression.args.get("on") else "OFF"
4570        filter_col: t.Optional[str] = self.sql(expression, "filter_column")
4571        filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
4572        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
4573        retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
4574
4575        if filter_col or retention_period:
4576            on_sql = self.func("ON", filter_col, retention_period)
4577
4578        return f"DATA_DELETION={on_sql}"
4579
4580    def maskingpolicycolumnconstraint_sql(
4581        self, expression: exp.MaskingPolicyColumnConstraint
4582    ) -> str:
4583        this = self.sql(expression, "this")
4584        expressions = self.expressions(expression, flat=True)
4585        expressions = f" USING ({expressions})" if expressions else ""
4586        return f"MASKING POLICY {this}{expressions}"
4587
4588    def gapfill_sql(self, expression: exp.GapFill) -> str:
4589        this = self.sql(expression, "this")
4590        this = f"TABLE {this}"
4591        return self.func("GAP_FILL", this, *[v for k, v in expression.args.items() if k != "this"])
4592
4593    def scope_resolution(self, rhs: str, scope_name: str) -> str:
4594        return self.func("SCOPE_RESOLUTION", scope_name or None, rhs)
4595
4596    def scoperesolution_sql(self, expression: exp.ScopeResolution) -> str:
4597        this = self.sql(expression, "this")
4598        expr = expression.expression
4599
4600        if isinstance(expr, exp.Func):
4601            # T-SQL's CLR functions are case sensitive
4602            expr = f"{self.sql(expr, 'this')}({self.format_args(*expr.expressions)})"
4603        else:
4604            expr = self.sql(expression, "expression")
4605
4606        return self.scope_resolution(expr, this)
4607
4608    def parsejson_sql(self, expression: exp.ParseJSON) -> str:
4609        if self.PARSE_JSON_NAME is None:
4610            return self.sql(expression.this)
4611
4612        return self.func(self.PARSE_JSON_NAME, expression.this, expression.expression)
4613
4614    def rand_sql(self, expression: exp.Rand) -> str:
4615        lower = self.sql(expression, "lower")
4616        upper = self.sql(expression, "upper")
4617
4618        if lower and upper:
4619            return f"({upper} - {lower}) * {self.func('RAND', expression.this)} + {lower}"
4620        return self.func("RAND", expression.this)
4621
4622    def changes_sql(self, expression: exp.Changes) -> str:
4623        information = self.sql(expression, "information")
4624        information = f"INFORMATION => {information}"
4625        at_before = self.sql(expression, "at_before")
4626        at_before = f"{self.seg('')}{at_before}" if at_before else ""
4627        end = self.sql(expression, "end")
4628        end = f"{self.seg('')}{end}" if end else ""
4629
4630        return f"CHANGES ({information}){at_before}{end}"
4631
4632    def pad_sql(self, expression: exp.Pad) -> str:
4633        prefix = "L" if expression.args.get("is_left") else "R"
4634
4635        fill_pattern = self.sql(expression, "fill_pattern") or None
4636        if not fill_pattern and self.PAD_FILL_PATTERN_IS_REQUIRED:
4637            fill_pattern = "' '"
4638
4639        return self.func(f"{prefix}PAD", expression.this, expression.expression, fill_pattern)
4640
4641    def summarize_sql(self, expression: exp.Summarize) -> str:
4642        table = " TABLE" if expression.args.get("table") else ""
4643        return f"SUMMARIZE{table} {self.sql(expression.this)}"
4644
4645    def explodinggenerateseries_sql(self, expression: exp.ExplodingGenerateSeries) -> str:
4646        generate_series = exp.GenerateSeries(**expression.args)
4647
4648        parent = expression.parent
4649        if isinstance(parent, (exp.Alias, exp.TableAlias)):
4650            parent = parent.parent
4651
4652        if self.SUPPORTS_EXPLODING_PROJECTIONS and not isinstance(parent, (exp.Table, exp.Unnest)):
4653            return self.sql(exp.Unnest(expressions=[generate_series]))
4654
4655        if isinstance(parent, exp.Select):
4656            self.unsupported("GenerateSeries projection unnesting is not supported.")
4657
4658        return self.sql(generate_series)
4659
4660    def arrayconcat_sql(self, expression: exp.ArrayConcat, name: str = "ARRAY_CONCAT") -> str:
4661        exprs = expression.expressions
4662        if not self.ARRAY_CONCAT_IS_VAR_LEN:
4663            rhs = reduce(lambda x, y: exp.ArrayConcat(this=x, expressions=[y]), exprs)
4664        else:
4665            rhs = self.expressions(expression)
4666
4667        return self.func(name, expression.this, rhs or None)
4668
4669    def converttimezone_sql(self, expression: exp.ConvertTimezone) -> str:
4670        if self.SUPPORTS_CONVERT_TIMEZONE:
4671            return self.function_fallback_sql(expression)
4672
4673        source_tz = expression.args.get("source_tz")
4674        target_tz = expression.args.get("target_tz")
4675        timestamp = expression.args.get("timestamp")
4676
4677        if source_tz and timestamp:
4678            timestamp = exp.AtTimeZone(
4679                this=exp.cast(timestamp, exp.DataType.Type.TIMESTAMPNTZ), zone=source_tz
4680            )
4681
4682        expr = exp.AtTimeZone(this=timestamp, zone=target_tz)
4683
4684        return self.sql(expr)
4685
4686    def json_sql(self, expression: exp.JSON) -> str:
4687        this = self.sql(expression, "this")
4688        this = f" {this}" if this else ""
4689
4690        _with = expression.args.get("with")
4691
4692        if _with is None:
4693            with_sql = ""
4694        elif not _with:
4695            with_sql = " WITHOUT"
4696        else:
4697            with_sql = " WITH"
4698
4699        unique_sql = " UNIQUE KEYS" if expression.args.get("unique") else ""
4700
4701        return f"JSON{this}{with_sql}{unique_sql}"
4702
4703    def jsonvalue_sql(self, expression: exp.JSONValue) -> str:
4704        def _generate_on_options(arg: t.Any) -> str:
4705            return arg if isinstance(arg, str) else f"DEFAULT {self.sql(arg)}"
4706
4707        path = self.sql(expression, "path")
4708        returning = self.sql(expression, "returning")
4709        returning = f" RETURNING {returning}" if returning else ""
4710
4711        on_condition = self.sql(expression, "on_condition")
4712        on_condition = f" {on_condition}" if on_condition else ""
4713
4714        return self.func("JSON_VALUE", expression.this, f"{path}{returning}{on_condition}")
4715
4716    def conditionalinsert_sql(self, expression: exp.ConditionalInsert) -> str:
4717        else_ = "ELSE " if expression.args.get("else_") else ""
4718        condition = self.sql(expression, "expression")
4719        condition = f"WHEN {condition} THEN " if condition else else_
4720        insert = self.sql(expression, "this")[len("INSERT") :].strip()
4721        return f"{condition}{insert}"
4722
4723    def multitableinserts_sql(self, expression: exp.MultitableInserts) -> str:
4724        kind = self.sql(expression, "kind")
4725        expressions = self.seg(self.expressions(expression, sep=" "))
4726        res = f"INSERT {kind}{expressions}{self.seg(self.sql(expression, 'source'))}"
4727        return res
4728
4729    def oncondition_sql(self, expression: exp.OnCondition) -> str:
4730        # Static options like "NULL ON ERROR" are stored as strings, in contrast to "DEFAULT <expr> ON ERROR"
4731        empty = expression.args.get("empty")
4732        empty = (
4733            f"DEFAULT {empty} ON EMPTY"
4734            if isinstance(empty, exp.Expression)
4735            else self.sql(expression, "empty")
4736        )
4737
4738        error = expression.args.get("error")
4739        error = (
4740            f"DEFAULT {error} ON ERROR"
4741            if isinstance(error, exp.Expression)
4742            else self.sql(expression, "error")
4743        )
4744
4745        if error and empty:
4746            error = (
4747                f"{empty} {error}"
4748                if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR
4749                else f"{error} {empty}"
4750            )
4751            empty = ""
4752
4753        null = self.sql(expression, "null")
4754
4755        return f"{empty}{error}{null}"
4756
4757    def jsonextractquote_sql(self, expression: exp.JSONExtractQuote) -> str:
4758        scalar = " ON SCALAR STRING" if expression.args.get("scalar") else ""
4759        return f"{self.sql(expression, 'option')} QUOTES{scalar}"
4760
4761    def jsonexists_sql(self, expression: exp.JSONExists) -> str:
4762        this = self.sql(expression, "this")
4763        path = self.sql(expression, "path")
4764
4765        passing = self.expressions(expression, "passing")
4766        passing = f" PASSING {passing}" if passing else ""
4767
4768        on_condition = self.sql(expression, "on_condition")
4769        on_condition = f" {on_condition}" if on_condition else ""
4770
4771        path = f"{path}{passing}{on_condition}"
4772
4773        return self.func("JSON_EXISTS", this, path)
4774
4775    def arrayagg_sql(self, expression: exp.ArrayAgg) -> str:
4776        array_agg = self.function_fallback_sql(expression)
4777
4778        # Add a NULL FILTER on the column to mimic the results going from a dialect that excludes nulls
4779        # on ARRAY_AGG (e.g Spark) to one that doesn't (e.g. DuckDB)
4780        if self.dialect.ARRAY_AGG_INCLUDES_NULLS and expression.args.get("nulls_excluded"):
4781            parent = expression.parent
4782            if isinstance(parent, exp.Filter):
4783                parent_cond = parent.expression.this
4784                parent_cond.replace(parent_cond.and_(expression.this.is_(exp.null()).not_()))
4785            else:
4786                this = expression.this
4787                # Do not add the filter if the input is not a column (e.g. literal, struct etc)
4788                if this.find(exp.Column):
4789                    # DISTINCT is already present in the agg function, do not propagate it to FILTER as well
4790                    this_sql = (
4791                        self.expressions(this)
4792                        if isinstance(this, exp.Distinct)
4793                        else self.sql(expression, "this")
4794                    )
4795
4796                    array_agg = f"{array_agg} FILTER(WHERE {this_sql} IS NOT NULL)"
4797
4798        return array_agg
4799
4800    def apply_sql(self, expression: exp.Apply) -> str:
4801        this = self.sql(expression, "this")
4802        expr = self.sql(expression, "expression")
4803
4804        return f"{this} APPLY({expr})"
4805
4806    def grant_sql(self, expression: exp.Grant) -> str:
4807        privileges_sql = self.expressions(expression, key="privileges", flat=True)
4808
4809        kind = self.sql(expression, "kind")
4810        kind = f" {kind}" if kind else ""
4811
4812        securable = self.sql(expression, "securable")
4813        securable = f" {securable}" if securable else ""
4814
4815        principals = self.expressions(expression, key="principals", flat=True)
4816
4817        grant_option = " WITH GRANT OPTION" if expression.args.get("grant_option") else ""
4818
4819        return f"GRANT {privileges_sql} ON{kind}{securable} TO {principals}{grant_option}"
4820
4821    def grantprivilege_sql(self, expression: exp.GrantPrivilege):
4822        this = self.sql(expression, "this")
4823        columns = self.expressions(expression, flat=True)
4824        columns = f"({columns})" if columns else ""
4825
4826        return f"{this}{columns}"
4827
4828    def grantprincipal_sql(self, expression: exp.GrantPrincipal):
4829        this = self.sql(expression, "this")
4830
4831        kind = self.sql(expression, "kind")
4832        kind = f"{kind} " if kind else ""
4833
4834        return f"{kind}{this}"
4835
4836    def columns_sql(self, expression: exp.Columns):
4837        func = self.function_fallback_sql(expression)
4838        if expression.args.get("unpack"):
4839            func = f"*{func}"
4840
4841        return func
4842
4843    def overlay_sql(self, expression: exp.Overlay):
4844        this = self.sql(expression, "this")
4845        expr = self.sql(expression, "expression")
4846        from_sql = self.sql(expression, "from")
4847        for_sql = self.sql(expression, "for")
4848        for_sql = f" FOR {for_sql}" if for_sql else ""
4849
4850        return f"OVERLAY({this} PLACING {expr} FROM {from_sql}{for_sql})"
4851
4852    @unsupported_args("format")
4853    def todouble_sql(self, expression: exp.ToDouble) -> str:
4854        return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
4855
4856    def string_sql(self, expression: exp.String) -> str:
4857        this = expression.this
4858        zone = expression.args.get("zone")
4859
4860        if zone:
4861            # This is a BigQuery specific argument for STRING(<timestamp_expr>, <time_zone>)
4862            # BigQuery stores timestamps internally as UTC, so ConvertTimezone is used with UTC
4863            # set for source_tz to transpile the time conversion before the STRING cast
4864            this = exp.ConvertTimezone(
4865                source_tz=exp.Literal.string("UTC"), target_tz=zone, timestamp=this
4866            )
4867
4868        return self.sql(exp.cast(this, exp.DataType.Type.VARCHAR))
4869
4870    def median_sql(self, expression: exp.Median):
4871        if not self.SUPPORTS_MEDIAN:
4872            return self.sql(
4873                exp.PercentileCont(this=expression.this, expression=exp.Literal.number(0.5))
4874            )
4875
4876        return self.function_fallback_sql(expression)
4877
4878    def overflowtruncatebehavior_sql(self, expression: exp.OverflowTruncateBehavior) -> str:
4879        filler = self.sql(expression, "this")
4880        filler = f" {filler}" if filler else ""
4881        with_count = "WITH COUNT" if expression.args.get("with_count") else "WITHOUT COUNT"
4882        return f"TRUNCATE{filler} {with_count}"
4883
4884    def unixseconds_sql(self, expression: exp.UnixSeconds) -> str:
4885        if self.SUPPORTS_UNIX_SECONDS:
4886            return self.function_fallback_sql(expression)
4887
4888        start_ts = exp.cast(
4889            exp.Literal.string("1970-01-01 00:00:00+00"), to=exp.DataType.Type.TIMESTAMPTZ
4890        )
4891
4892        return self.sql(
4893            exp.TimestampDiff(this=expression.this, expression=start_ts, unit=exp.var("SECONDS"))
4894        )
4895
4896    def arraysize_sql(self, expression: exp.ArraySize) -> str:
4897        dim = expression.expression
4898
4899        # For dialects that don't support the dimension arg, we can safely transpile it's default value (1st dimension)
4900        if dim and self.ARRAY_SIZE_DIM_REQUIRED is None:
4901            if not (dim.is_int and dim.name == "1"):
4902                self.unsupported("Cannot transpile dimension argument for ARRAY_LENGTH")
4903            dim = None
4904
4905        # If dimension is required but not specified, default initialize it
4906        if self.ARRAY_SIZE_DIM_REQUIRED and not dim:
4907            dim = exp.Literal.number(1)
4908
4909        return self.func(self.ARRAY_SIZE_NAME, expression.this, dim)
4910
4911    def attach_sql(self, expression: exp.Attach) -> str:
4912        this = self.sql(expression, "this")
4913        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
4914        expressions = self.expressions(expression)
4915        expressions = f" ({expressions})" if expressions else ""
4916
4917        return f"ATTACH{exists_sql} {this}{expressions}"
4918
4919    def detach_sql(self, expression: exp.Detach) -> str:
4920        this = self.sql(expression, "this")
4921        # the DATABASE keyword is required if IF EXISTS is set
4922        # without it, DuckDB throws an error: Parser Error: syntax error at or near "exists" (Line Number: 1)
4923        # ref: https://duckdb.org/docs/stable/sql/statements/attach.html#detach-syntax
4924        exists_sql = " DATABASE IF EXISTS" if expression.args.get("exists") else ""
4925
4926        return f"DETACH{exists_sql} {this}"
4927
4928    def attachoption_sql(self, expression: exp.AttachOption) -> str:
4929        this = self.sql(expression, "this")
4930        value = self.sql(expression, "expression")
4931        value = f" {value}" if value else ""
4932        return f"{this}{value}"
4933
4934    def watermarkcolumnconstraint_sql(self, expression: exp.WatermarkColumnConstraint) -> str:
4935        return (
4936            f"WATERMARK FOR {self.sql(expression, 'this')} AS {self.sql(expression, 'expression')}"
4937        )
4938
4939    def encodeproperty_sql(self, expression: exp.EncodeProperty) -> str:
4940        encode = "KEY ENCODE" if expression.args.get("key") else "ENCODE"
4941        encode = f"{encode} {self.sql(expression, 'this')}"
4942
4943        properties = expression.args.get("properties")
4944        if properties:
4945            encode = f"{encode} {self.properties(properties)}"
4946
4947        return encode
4948
4949    def includeproperty_sql(self, expression: exp.IncludeProperty) -> str:
4950        this = self.sql(expression, "this")
4951        include = f"INCLUDE {this}"
4952
4953        column_def = self.sql(expression, "column_def")
4954        if column_def:
4955            include = f"{include} {column_def}"
4956
4957        alias = self.sql(expression, "alias")
4958        if alias:
4959            include = f"{include} AS {alias}"
4960
4961        return include
4962
4963    def xmlelement_sql(self, expression: exp.XMLElement) -> str:
4964        name = f"NAME {self.sql(expression, 'this')}"
4965        return self.func("XMLELEMENT", name, *expression.expressions)
4966
4967    def xmlkeyvalueoption_sql(self, expression: exp.XMLKeyValueOption) -> str:
4968        this = self.sql(expression, "this")
4969        expr = self.sql(expression, "expression")
4970        expr = f"({expr})" if expr else ""
4971        return f"{this}{expr}"
4972
4973    def partitionbyrangeproperty_sql(self, expression: exp.PartitionByRangeProperty) -> str:
4974        partitions = self.expressions(expression, "partition_expressions")
4975        create = self.expressions(expression, "create_expressions")
4976        return f"PARTITION BY RANGE {self.wrap(partitions)} {self.wrap(create)}"
4977
4978    def partitionbyrangepropertydynamic_sql(
4979        self, expression: exp.PartitionByRangePropertyDynamic
4980    ) -> str:
4981        start = self.sql(expression, "start")
4982        end = self.sql(expression, "end")
4983
4984        every = expression.args["every"]
4985        if isinstance(every, exp.Interval) and every.this.is_string:
4986            every.this.replace(exp.Literal.number(every.name))
4987
4988        return f"START {self.wrap(start)} END {self.wrap(end)} EVERY {self.wrap(self.sql(every))}"
4989
4990    def unpivotcolumns_sql(self, expression: exp.UnpivotColumns) -> str:
4991        name = self.sql(expression, "this")
4992        values = self.expressions(expression, flat=True)
4993
4994        return f"NAME {name} VALUE {values}"
4995
4996    def analyzesample_sql(self, expression: exp.AnalyzeSample) -> str:
4997        kind = self.sql(expression, "kind")
4998        sample = self.sql(expression, "sample")
4999        return f"SAMPLE {sample} {kind}"
5000
5001    def analyzestatistics_sql(self, expression: exp.AnalyzeStatistics) -> str:
5002        kind = self.sql(expression, "kind")
5003        option = self.sql(expression, "option")
5004        option = f" {option}" if option else ""
5005        this = self.sql(expression, "this")
5006        this = f" {this}" if this else ""
5007        columns = self.expressions(expression)
5008        columns = f" {columns}" if columns else ""
5009        return f"{kind}{option} STATISTICS{this}{columns}"
5010
5011    def analyzehistogram_sql(self, expression: exp.AnalyzeHistogram) -> str:
5012        this = self.sql(expression, "this")
5013        columns = self.expressions(expression)
5014        inner_expression = self.sql(expression, "expression")
5015        inner_expression = f" {inner_expression}" if inner_expression else ""
5016        update_options = self.sql(expression, "update_options")
5017        update_options = f" {update_options} UPDATE" if update_options else ""
5018        return f"{this} HISTOGRAM ON {columns}{inner_expression}{update_options}"
5019
5020    def analyzedelete_sql(self, expression: exp.AnalyzeDelete) -> str:
5021        kind = self.sql(expression, "kind")
5022        kind = f" {kind}" if kind else ""
5023        return f"DELETE{kind} STATISTICS"
5024
5025    def analyzelistchainedrows_sql(self, expression: exp.AnalyzeListChainedRows) -> str:
5026        inner_expression = self.sql(expression, "expression")
5027        return f"LIST CHAINED ROWS{inner_expression}"
5028
5029    def analyzevalidate_sql(self, expression: exp.AnalyzeValidate) -> str:
5030        kind = self.sql(expression, "kind")
5031        this = self.sql(expression, "this")
5032        this = f" {this}" if this else ""
5033        inner_expression = self.sql(expression, "expression")
5034        return f"VALIDATE {kind}{this}{inner_expression}"
5035
5036    def analyze_sql(self, expression: exp.Analyze) -> str:
5037        options = self.expressions(expression, key="options", sep=" ")
5038        options = f" {options}" if options else ""
5039        kind = self.sql(expression, "kind")
5040        kind = f" {kind}" if kind else ""
5041        this = self.sql(expression, "this")
5042        this = f" {this}" if this else ""
5043        mode = self.sql(expression, "mode")
5044        mode = f" {mode}" if mode else ""
5045        properties = self.sql(expression, "properties")
5046        properties = f" {properties}" if properties else ""
5047        partition = self.sql(expression, "partition")
5048        partition = f" {partition}" if partition else ""
5049        inner_expression = self.sql(expression, "expression")
5050        inner_expression = f" {inner_expression}" if inner_expression else ""
5051        return f"ANALYZE{options}{kind}{this}{partition}{mode}{inner_expression}{properties}"
5052
5053    def xmltable_sql(self, expression: exp.XMLTable) -> str:
5054        this = self.sql(expression, "this")
5055        namespaces = self.expressions(expression, key="namespaces")
5056        namespaces = f"XMLNAMESPACES({namespaces}), " if namespaces else ""
5057        passing = self.expressions(expression, key="passing")
5058        passing = f"{self.sep()}PASSING{self.seg(passing)}" if passing else ""
5059        columns = self.expressions(expression, key="columns")
5060        columns = f"{self.sep()}COLUMNS{self.seg(columns)}" if columns else ""
5061        by_ref = f"{self.sep()}RETURNING SEQUENCE BY REF" if expression.args.get("by_ref") else ""
5062        return f"XMLTABLE({self.sep('')}{self.indent(namespaces + this + passing + by_ref + columns)}{self.seg(')', sep='')}"
5063
5064    def xmlnamespace_sql(self, expression: exp.XMLNamespace) -> str:
5065        this = self.sql(expression, "this")
5066        return this if isinstance(expression.this, exp.Alias) else f"DEFAULT {this}"
5067
5068    def export_sql(self, expression: exp.Export) -> str:
5069        this = self.sql(expression, "this")
5070        connection = self.sql(expression, "connection")
5071        connection = f"WITH CONNECTION {connection} " if connection else ""
5072        options = self.sql(expression, "options")
5073        return f"EXPORT DATA {connection}{options} AS {this}"
5074
5075    def declare_sql(self, expression: exp.Declare) -> str:
5076        return f"DECLARE {self.expressions(expression, flat=True)}"
5077
5078    def declareitem_sql(self, expression: exp.DeclareItem) -> str:
5079        variable = self.sql(expression, "this")
5080        default = self.sql(expression, "default")
5081        default = f" = {default}" if default else ""
5082
5083        kind = self.sql(expression, "kind")
5084        if isinstance(expression.args.get("kind"), exp.Schema):
5085            kind = f"TABLE {kind}"
5086
5087        return f"{variable} AS {kind}{default}"
5088
5089    def recursivewithsearch_sql(self, expression: exp.RecursiveWithSearch) -> str:
5090        kind = self.sql(expression, "kind")
5091        this = self.sql(expression, "this")
5092        set = self.sql(expression, "expression")
5093        using = self.sql(expression, "using")
5094        using = f" USING {using}" if using else ""
5095
5096        kind_sql = kind if kind == "CYCLE" else f"SEARCH {kind} FIRST BY"
5097
5098        return f"{kind_sql} {this} SET {set}{using}"
5099
5100    def parameterizedagg_sql(self, expression: exp.ParameterizedAgg) -> str:
5101        params = self.expressions(expression, key="params", flat=True)
5102        return self.func(expression.name, *expression.expressions) + f"({params})"
5103
5104    def anonymousaggfunc_sql(self, expression: exp.AnonymousAggFunc) -> str:
5105        return self.func(expression.name, *expression.expressions)
5106
5107    def combinedaggfunc_sql(self, expression: exp.CombinedAggFunc) -> str:
5108        return self.anonymousaggfunc_sql(expression)
5109
5110    def combinedparameterizedagg_sql(self, expression: exp.CombinedParameterizedAgg) -> str:
5111        return self.parameterizedagg_sql(expression)
5112
5113    def show_sql(self, expression: exp.Show) -> str:
5114        self.unsupported("Unsupported SHOW statement")
5115        return ""
5116
5117    def get_put_sql(self, expression: exp.Put | exp.Get) -> str:
5118        # Snowflake GET/PUT statements:
5119        #   PUT <file> <internalStage> <properties>
5120        #   GET <internalStage> <file> <properties>
5121        props = expression.args.get("properties")
5122        props_sql = self.properties(props, prefix=" ", sep=" ", wrapped=False) if props else ""
5123        this = self.sql(expression, "this")
5124        target = self.sql(expression, "target")
5125
5126        if isinstance(expression, exp.Put):
5127            return f"PUT {this} {target}{props_sql}"
5128        else:
5129            return f"GET {target} {this}{props_sql}"
5130
5131    def translatecharacters_sql(self, expression: exp.TranslateCharacters):
5132        this = self.sql(expression, "this")
5133        expr = self.sql(expression, "expression")
5134        with_error = " WITH ERROR" if expression.args.get("with_error") else ""
5135        return f"TRANSLATE({this} USING {expr}{with_error})"
5136
5137    def decodecase_sql(self, expression: exp.DecodeCase) -> str:
5138        if self.SUPPORTS_DECODE_CASE:
5139            return self.func("DECODE", *expression.expressions)
5140
5141        expression, *expressions = expression.expressions
5142
5143        ifs = []
5144        for search, result in zip(expressions[::2], expressions[1::2]):
5145            if isinstance(search, exp.Literal):
5146                ifs.append(exp.If(this=expression.eq(search), true=result))
5147            elif isinstance(search, exp.Null):
5148                ifs.append(exp.If(this=expression.is_(exp.Null()), true=result))
5149            else:
5150                if isinstance(search, exp.Binary):
5151                    search = exp.paren(search)
5152
5153                cond = exp.or_(
5154                    expression.eq(search),
5155                    exp.and_(expression.is_(exp.Null()), search.is_(exp.Null()), copy=False),
5156                    copy=False,
5157                )
5158                ifs.append(exp.If(this=cond, true=result))
5159
5160        case = exp.Case(ifs=ifs, default=expressions[-1] if len(expressions) % 2 == 1 else None)
5161        return self.sql(case)
5162
5163    def semanticview_sql(self, expression: exp.SemanticView) -> str:
5164        this = self.sql(expression, "this")
5165        this = self.seg(this, sep="")
5166        dimensions = self.expressions(
5167            expression, "dimensions", dynamic=True, skip_first=True, skip_last=True
5168        )
5169        dimensions = self.seg(f"DIMENSIONS {dimensions}") if dimensions else ""
5170        metrics = self.expressions(
5171            expression, "metrics", dynamic=True, skip_first=True, skip_last=True
5172        )
5173        metrics = self.seg(f"METRICS {metrics}") if metrics else ""
5174        where = self.sql(expression, "where")
5175        where = self.seg(f"WHERE {where}") if where else ""
5176        return f"SEMANTIC_VIEW({self.indent(this + metrics + dimensions + where)}{self.seg(')', sep='')}"
5177
5178    def getextract_sql(self, expression: exp.GetExtract) -> str:
5179        this = expression.this
5180        expr = expression.expression
5181
5182        if not this.type or not expression.type:
5183            from sqlglot.optimizer.annotate_types import annotate_types
5184
5185            this = annotate_types(this, dialect=self.dialect)
5186
5187        if this.is_type(*(exp.DataType.Type.ARRAY, exp.DataType.Type.MAP)):
5188            return self.sql(exp.Bracket(this=this, expressions=[expr]))
5189
5190        return self.sql(exp.JSONExtract(this=this, expression=self.dialect.to_json_path(expr)))
5191
5192    def datefromunixdate_sql(self, expression: exp.DateFromUnixDate) -> str:
5193        return self.sql(
5194            exp.DateAdd(
5195                this=exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
5196                expression=expression.this,
5197                unit=exp.var("DAY"),
5198            )
5199        )
5200
5201    def space_sql(self: Generator, expression: exp.Space) -> str:
5202        return self.sql(exp.Repeat(this=exp.Literal.string(" "), times=expression.this))
5203
5204    def buildproperty_sql(self, expression: exp.BuildProperty) -> str:
5205        return f"BUILD {self.sql(expression, 'this')}"
5206
5207    def refreshtriggerproperty_sql(self, expression: exp.RefreshTriggerProperty) -> str:
5208        method = self.sql(expression, "method")
5209        kind = expression.args.get("kind")
5210        if not kind:
5211            return f"REFRESH {method}"
5212
5213        every = self.sql(expression, "every")
5214        unit = self.sql(expression, "unit")
5215        every = f" EVERY {every} {unit}" if every else ""
5216        starts = self.sql(expression, "starts")
5217        starts = f" STARTS {starts}" if starts else ""
5218
5219        return f"REFRESH {method} ON {kind}{every}{starts}"
logger = <Logger sqlglot (WARNING)>
ESCAPED_UNICODE_RE = re.compile('\\\\(\\d+)')
UNSUPPORTED_TEMPLATE = "Argument '{}' is not supported for expression '{}' when targeting {}."
def unsupported_args( *args: Union[str, Tuple[str, str]]) -> Callable[[Callable[[~G, ~E], str]], Callable[[~G, ~E], str]]:
30def unsupported_args(
31    *args: t.Union[str, t.Tuple[str, str]],
32) -> t.Callable[[GeneratorMethod], GeneratorMethod]:
33    """
34    Decorator that can be used to mark certain args of an `Expression` subclass as unsupported.
35    It expects a sequence of argument names or pairs of the form (argument_name, diagnostic_msg).
36    """
37    diagnostic_by_arg: t.Dict[str, t.Optional[str]] = {}
38    for arg in args:
39        if isinstance(arg, str):
40            diagnostic_by_arg[arg] = None
41        else:
42            diagnostic_by_arg[arg[0]] = arg[1]
43
44    def decorator(func: GeneratorMethod) -> GeneratorMethod:
45        @wraps(func)
46        def _func(generator: G, expression: E) -> str:
47            expression_name = expression.__class__.__name__
48            dialect_name = generator.dialect.__class__.__name__
49
50            for arg_name, diagnostic in diagnostic_by_arg.items():
51                if expression.args.get(arg_name):
52                    diagnostic = diagnostic or UNSUPPORTED_TEMPLATE.format(
53                        arg_name, expression_name, dialect_name
54                    )
55                    generator.unsupported(diagnostic)
56
57            return func(generator, expression)
58
59        return _func
60
61    return decorator

Decorator that can be used to mark certain args of an Expression subclass as unsupported. It expects a sequence of argument names or pairs of the form (argument_name, diagnostic_msg).

class Generator:
  75class Generator(metaclass=_Generator):
  76    """
  77    Generator converts a given syntax tree to the corresponding SQL string.
  78
  79    Args:
  80        pretty: Whether to format the produced SQL string.
  81            Default: False.
  82        identify: Determines when an identifier should be quoted. Possible values are:
  83            False (default): Never quote, except in cases where it's mandatory by the dialect.
  84            True or 'always': Always quote.
  85            'safe': Only quote identifiers that are case insensitive.
  86        normalize: Whether to normalize identifiers to lowercase.
  87            Default: False.
  88        pad: The pad size in a formatted string. For example, this affects the indentation of
  89            a projection in a query, relative to its nesting level.
  90            Default: 2.
  91        indent: The indentation size in a formatted string. For example, this affects the
  92            indentation of subqueries and filters under a `WHERE` clause.
  93            Default: 2.
  94        normalize_functions: How to normalize function names. Possible values are:
  95            "upper" or True (default): Convert names to uppercase.
  96            "lower": Convert names to lowercase.
  97            False: Disables function name normalization.
  98        unsupported_level: Determines the generator's behavior when it encounters unsupported expressions.
  99            Default ErrorLevel.WARN.
 100        max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError.
 101            This is only relevant if unsupported_level is ErrorLevel.RAISE.
 102            Default: 3
 103        leading_comma: Whether the comma is leading or trailing in select expressions.
 104            This is only relevant when generating in pretty mode.
 105            Default: False
 106        max_text_width: The max number of characters in a segment before creating new lines in pretty mode.
 107            The default is on the smaller end because the length only represents a segment and not the true
 108            line length.
 109            Default: 80
 110        comments: Whether to preserve comments in the output SQL code.
 111            Default: True
 112    """
 113
 114    TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = {
 115        **JSON_PATH_PART_TRANSFORMS,
 116        exp.AllowedValuesProperty: lambda self,
 117        e: f"ALLOWED_VALUES {self.expressions(e, flat=True)}",
 118        exp.AnalyzeColumns: lambda self, e: self.sql(e, "this"),
 119        exp.AnalyzeWith: lambda self, e: self.expressions(e, prefix="WITH ", sep=" "),
 120        exp.ArrayContainsAll: lambda self, e: self.binary(e, "@>"),
 121        exp.ArrayOverlaps: lambda self, e: self.binary(e, "&&"),
 122        exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}",
 123        exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}",
 124        exp.CaseSpecificColumnConstraint: lambda _,
 125        e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
 126        exp.Ceil: lambda self, e: self.ceil_floor(e),
 127        exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
 128        exp.CharacterSetProperty: lambda self,
 129        e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
 130        exp.ClusteredColumnConstraint: lambda self,
 131        e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})",
 132        exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
 133        exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
 134        exp.ConnectByRoot: lambda self, e: f"CONNECT_BY_ROOT {self.sql(e, 'this')}",
 135        exp.ConvertToCharset: lambda self, e: self.func(
 136            "CONVERT", e.this, e.args["dest"], e.args.get("source")
 137        ),
 138        exp.CopyGrantsProperty: lambda *_: "COPY GRANTS",
 139        exp.CredentialsProperty: lambda self,
 140        e: f"CREDENTIALS=({self.expressions(e, 'expressions', sep=' ')})",
 141        exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
 142        exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
 143        exp.DynamicProperty: lambda *_: "DYNAMIC",
 144        exp.EmptyProperty: lambda *_: "EMPTY",
 145        exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
 146        exp.EnviromentProperty: lambda self, e: f"ENVIRONMENT ({self.expressions(e, flat=True)})",
 147        exp.EphemeralColumnConstraint: lambda self,
 148        e: f"EPHEMERAL{(' ' + self.sql(e, 'this')) if e.this else ''}",
 149        exp.ExcludeColumnConstraint: lambda self, e: f"EXCLUDE {self.sql(e, 'this').lstrip()}",
 150        exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
 151        exp.Except: lambda self, e: self.set_operations(e),
 152        exp.ExternalProperty: lambda *_: "EXTERNAL",
 153        exp.Floor: lambda self, e: self.ceil_floor(e),
 154        exp.Get: lambda self, e: self.get_put_sql(e),
 155        exp.GlobalProperty: lambda *_: "GLOBAL",
 156        exp.HeapProperty: lambda *_: "HEAP",
 157        exp.IcebergProperty: lambda *_: "ICEBERG",
 158        exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})",
 159        exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
 160        exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}",
 161        exp.Intersect: lambda self, e: self.set_operations(e),
 162        exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}",
 163        exp.Int64: lambda self, e: self.sql(exp.cast(e.this, exp.DataType.Type.BIGINT)),
 164        exp.LanguageProperty: lambda self, e: self.naked_property(e),
 165        exp.LocationProperty: lambda self, e: self.naked_property(e),
 166        exp.LogProperty: lambda _, e: f"{'NO ' if e.args.get('no') else ''}LOG",
 167        exp.MaterializedProperty: lambda *_: "MATERIALIZED",
 168        exp.NonClusteredColumnConstraint: lambda self,
 169        e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})",
 170        exp.NoPrimaryIndexProperty: lambda *_: "NO PRIMARY INDEX",
 171        exp.NotForReplicationColumnConstraint: lambda *_: "NOT FOR REPLICATION",
 172        exp.OnCommitProperty: lambda _,
 173        e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS",
 174        exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}",
 175        exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}",
 176        exp.Operator: lambda self, e: self.binary(e, ""),  # The operator is produced in `binary`
 177        exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}",
 178        exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
 179        exp.PartitionedByBucket: lambda self, e: self.func("BUCKET", e.this, e.expression),
 180        exp.PartitionByTruncate: lambda self, e: self.func("TRUNCATE", e.this, e.expression),
 181        exp.PivotAny: lambda self, e: f"ANY{self.sql(e, 'this')}",
 182        exp.PositionalColumn: lambda self, e: f"#{self.sql(e, 'this')}",
 183        exp.ProjectionPolicyColumnConstraint: lambda self,
 184        e: f"PROJECTION POLICY {self.sql(e, 'this')}",
 185        exp.Put: lambda self, e: self.get_put_sql(e),
 186        exp.RemoteWithConnectionModelProperty: lambda self,
 187        e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}",
 188        exp.ReturnsProperty: lambda self, e: (
 189            "RETURNS NULL ON NULL INPUT" if e.args.get("null") else self.naked_property(e)
 190        ),
 191        exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}",
 192        exp.SecureProperty: lambda *_: "SECURE",
 193        exp.SecurityProperty: lambda self, e: f"SECURITY {self.sql(e, 'this')}",
 194        exp.SetConfigProperty: lambda self, e: self.sql(e, "this"),
 195        exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
 196        exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}",
 197        exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}",
 198        exp.SqlReadWriteProperty: lambda _, e: e.name,
 199        exp.SqlSecurityProperty: lambda _,
 200        e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
 201        exp.StabilityProperty: lambda _, e: e.name,
 202        exp.Stream: lambda self, e: f"STREAM {self.sql(e, 'this')}",
 203        exp.StreamingTableProperty: lambda *_: "STREAMING",
 204        exp.StrictProperty: lambda *_: "STRICT",
 205        exp.SwapTable: lambda self, e: f"SWAP WITH {self.sql(e, 'this')}",
 206        exp.TableColumn: lambda self, e: self.sql(e.this),
 207        exp.Tags: lambda self, e: f"TAG ({self.expressions(e, flat=True)})",
 208        exp.TemporaryProperty: lambda *_: "TEMPORARY",
 209        exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
 210        exp.ToMap: lambda self, e: f"MAP {self.sql(e, 'this')}",
 211        exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}",
 212        exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions),
 213        exp.TransientProperty: lambda *_: "TRANSIENT",
 214        exp.Union: lambda self, e: self.set_operations(e),
 215        exp.UnloggedProperty: lambda *_: "UNLOGGED",
 216        exp.UsingTemplateProperty: lambda self, e: f"USING TEMPLATE {self.sql(e, 'this')}",
 217        exp.UsingData: lambda self, e: f"USING DATA {self.sql(e, 'this')}",
 218        exp.Uuid: lambda *_: "UUID()",
 219        exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE",
 220        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
 221        exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}",
 222        exp.VolatileProperty: lambda *_: "VOLATILE",
 223        exp.WeekStart: lambda self, e: f"WEEK({self.sql(e, 'this')})",
 224        exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
 225        exp.WithProcedureOptions: lambda self, e: f"WITH {self.expressions(e, flat=True)}",
 226        exp.WithSchemaBindingProperty: lambda self, e: f"WITH SCHEMA {self.sql(e, 'this')}",
 227        exp.WithOperator: lambda self, e: f"{self.sql(e, 'this')} WITH {self.sql(e, 'op')}",
 228        exp.ForceProperty: lambda *_: "FORCE",
 229    }
 230
 231    # Whether null ordering is supported in order by
 232    # True: Full Support, None: No support, False: No support for certain cases
 233    # such as window specifications, aggregate functions etc
 234    NULL_ORDERING_SUPPORTED: t.Optional[bool] = True
 235
 236    # Whether ignore nulls is inside the agg or outside.
 237    # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER
 238    IGNORE_NULLS_IN_FUNC = False
 239
 240    # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
 241    LOCKING_READS_SUPPORTED = False
 242
 243    # Whether the EXCEPT and INTERSECT operations can return duplicates
 244    EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = True
 245
 246    # Wrap derived values in parens, usually standard but spark doesn't support it
 247    WRAP_DERIVED_VALUES = True
 248
 249    # Whether create function uses an AS before the RETURN
 250    CREATE_FUNCTION_RETURN_AS = True
 251
 252    # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed
 253    MATCHED_BY_SOURCE = True
 254
 255    # Whether the INTERVAL expression works only with values like '1 day'
 256    SINGLE_STRING_INTERVAL = False
 257
 258    # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs
 259    INTERVAL_ALLOWS_PLURAL_FORM = True
 260
 261    # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH")
 262    LIMIT_FETCH = "ALL"
 263
 264    # Whether limit and fetch allows expresions or just limits
 265    LIMIT_ONLY_LITERALS = False
 266
 267    # Whether a table is allowed to be renamed with a db
 268    RENAME_TABLE_WITH_DB = True
 269
 270    # The separator for grouping sets and rollups
 271    GROUPINGS_SEP = ","
 272
 273    # The string used for creating an index on a table
 274    INDEX_ON = "ON"
 275
 276    # Whether join hints should be generated
 277    JOIN_HINTS = True
 278
 279    # Whether table hints should be generated
 280    TABLE_HINTS = True
 281
 282    # Whether query hints should be generated
 283    QUERY_HINTS = True
 284
 285    # What kind of separator to use for query hints
 286    QUERY_HINT_SEP = ", "
 287
 288    # Whether comparing against booleans (e.g. x IS TRUE) is supported
 289    IS_BOOL_ALLOWED = True
 290
 291    # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement
 292    DUPLICATE_KEY_UPDATE_WITH_SET = True
 293
 294    # Whether to generate the limit as TOP <value> instead of LIMIT <value>
 295    LIMIT_IS_TOP = False
 296
 297    # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ...
 298    RETURNING_END = True
 299
 300    # Whether to generate an unquoted value for EXTRACT's date part argument
 301    EXTRACT_ALLOWS_QUOTES = True
 302
 303    # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax
 304    TZ_TO_WITH_TIME_ZONE = False
 305
 306    # Whether the NVL2 function is supported
 307    NVL2_SUPPORTED = True
 308
 309    # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax
 310    SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE")
 311
 312    # Whether VALUES statements can be used as derived tables.
 313    # MySQL 5 and Redshift do not allow this, so when False, it will convert
 314    # SELECT * VALUES into SELECT UNION
 315    VALUES_AS_TABLE = True
 316
 317    # Whether the word COLUMN is included when adding a column with ALTER TABLE
 318    ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
 319
 320    # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
 321    UNNEST_WITH_ORDINALITY = True
 322
 323    # Whether FILTER (WHERE cond) can be used for conditional aggregation
 324    AGGREGATE_FILTER_SUPPORTED = True
 325
 326    # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds
 327    SEMI_ANTI_JOIN_WITH_SIDE = True
 328
 329    # Whether to include the type of a computed column in the CREATE DDL
 330    COMPUTED_COLUMN_WITH_TYPE = True
 331
 332    # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY
 333    SUPPORTS_TABLE_COPY = True
 334
 335    # Whether parentheses are required around the table sample's expression
 336    TABLESAMPLE_REQUIRES_PARENS = True
 337
 338    # Whether a table sample clause's size needs to be followed by the ROWS keyword
 339    TABLESAMPLE_SIZE_IS_ROWS = True
 340
 341    # The keyword(s) to use when generating a sample clause
 342    TABLESAMPLE_KEYWORDS = "TABLESAMPLE"
 343
 344    # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI
 345    TABLESAMPLE_WITH_METHOD = True
 346
 347    # The keyword to use when specifying the seed of a sample clause
 348    TABLESAMPLE_SEED_KEYWORD = "SEED"
 349
 350    # Whether COLLATE is a function instead of a binary operator
 351    COLLATE_IS_FUNC = False
 352
 353    # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle)
 354    DATA_TYPE_SPECIFIERS_ALLOWED = False
 355
 356    # Whether conditions require booleans WHERE x = 0 vs WHERE x
 357    ENSURE_BOOLS = False
 358
 359    # Whether the "RECURSIVE" keyword is required when defining recursive CTEs
 360    CTE_RECURSIVE_KEYWORD_REQUIRED = True
 361
 362    # Whether CONCAT requires >1 arguments
 363    SUPPORTS_SINGLE_ARG_CONCAT = True
 364
 365    # Whether LAST_DAY function supports a date part argument
 366    LAST_DAY_SUPPORTS_DATE_PART = True
 367
 368    # Whether named columns are allowed in table aliases
 369    SUPPORTS_TABLE_ALIAS_COLUMNS = True
 370
 371    # Whether UNPIVOT aliases are Identifiers (False means they're Literals)
 372    UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
 373
 374    # What delimiter to use for separating JSON key/value pairs
 375    JSON_KEY_VALUE_PAIR_SEP = ":"
 376
 377    # INSERT OVERWRITE TABLE x override
 378    INSERT_OVERWRITE = " OVERWRITE TABLE"
 379
 380    # Whether the SELECT .. INTO syntax is used instead of CTAS
 381    SUPPORTS_SELECT_INTO = False
 382
 383    # Whether UNLOGGED tables can be created
 384    SUPPORTS_UNLOGGED_TABLES = False
 385
 386    # Whether the CREATE TABLE LIKE statement is supported
 387    SUPPORTS_CREATE_TABLE_LIKE = True
 388
 389    # Whether the LikeProperty needs to be specified inside of the schema clause
 390    LIKE_PROPERTY_INSIDE_SCHEMA = False
 391
 392    # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be
 393    # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args
 394    MULTI_ARG_DISTINCT = True
 395
 396    # Whether the JSON extraction operators expect a value of type JSON
 397    JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
 398
 399    # Whether bracketed keys like ["foo"] are supported in JSON paths
 400    JSON_PATH_BRACKETED_KEY_SUPPORTED = True
 401
 402    # Whether to escape keys using single quotes in JSON paths
 403    JSON_PATH_SINGLE_QUOTE_ESCAPE = False
 404
 405    # The JSONPathPart expressions supported by this dialect
 406    SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy()
 407
 408    # Whether any(f(x) for x in array) can be implemented by this dialect
 409    CAN_IMPLEMENT_ARRAY_ANY = False
 410
 411    # Whether the function TO_NUMBER is supported
 412    SUPPORTS_TO_NUMBER = True
 413
 414    # Whether EXCLUDE in window specification is supported
 415    SUPPORTS_WINDOW_EXCLUDE = False
 416
 417    # Whether or not set op modifiers apply to the outer set op or select.
 418    # SELECT * FROM x UNION SELECT * FROM y LIMIT 1
 419    # True means limit 1 happens after the set op, False means it it happens on y.
 420    SET_OP_MODIFIERS = True
 421
 422    # Whether parameters from COPY statement are wrapped in parentheses
 423    COPY_PARAMS_ARE_WRAPPED = True
 424
 425    # Whether values of params are set with "=" token or empty space
 426    COPY_PARAMS_EQ_REQUIRED = False
 427
 428    # Whether COPY statement has INTO keyword
 429    COPY_HAS_INTO_KEYWORD = True
 430
 431    # Whether the conditional TRY(expression) function is supported
 432    TRY_SUPPORTED = True
 433
 434    # Whether the UESCAPE syntax in unicode strings is supported
 435    SUPPORTS_UESCAPE = True
 436
 437    # The keyword to use when generating a star projection with excluded columns
 438    STAR_EXCEPT = "EXCEPT"
 439
 440    # The HEX function name
 441    HEX_FUNC = "HEX"
 442
 443    # The keywords to use when prefixing & separating WITH based properties
 444    WITH_PROPERTIES_PREFIX = "WITH"
 445
 446    # Whether to quote the generated expression of exp.JsonPath
 447    QUOTE_JSON_PATH = True
 448
 449    # Whether the text pattern/fill (3rd) parameter of RPAD()/LPAD() is optional (defaults to space)
 450    PAD_FILL_PATTERN_IS_REQUIRED = False
 451
 452    # Whether a projection can explode into multiple rows, e.g. by unnesting an array.
 453    SUPPORTS_EXPLODING_PROJECTIONS = True
 454
 455    # Whether ARRAY_CONCAT can be generated with varlen args or if it should be reduced to 2-arg version
 456    ARRAY_CONCAT_IS_VAR_LEN = True
 457
 458    # Whether CONVERT_TIMEZONE() is supported; if not, it will be generated as exp.AtTimeZone
 459    SUPPORTS_CONVERT_TIMEZONE = False
 460
 461    # Whether MEDIAN(expr) is supported; if not, it will be generated as PERCENTILE_CONT(expr, 0.5)
 462    SUPPORTS_MEDIAN = True
 463
 464    # Whether UNIX_SECONDS(timestamp) is supported
 465    SUPPORTS_UNIX_SECONDS = False
 466
 467    # Whether to wrap <props> in `AlterSet`, e.g., ALTER ... SET (<props>)
 468    ALTER_SET_WRAPPED = False
 469
 470    # Whether to normalize the date parts in EXTRACT(<date_part> FROM <expr>) into a common representation
 471    # For instance, to extract the day of week in ISO semantics, one can use ISODOW, DAYOFWEEKISO etc depending on the dialect.
 472    # TODO: The normalization should be done by default once we've tested it across all dialects.
 473    NORMALIZE_EXTRACT_DATE_PARTS = False
 474
 475    # The name to generate for the JSONPath expression. If `None`, only `this` will be generated
 476    PARSE_JSON_NAME: t.Optional[str] = "PARSE_JSON"
 477
 478    # The function name of the exp.ArraySize expression
 479    ARRAY_SIZE_NAME: str = "ARRAY_LENGTH"
 480
 481    # The syntax to use when altering the type of a column
 482    ALTER_SET_TYPE = "SET DATA TYPE"
 483
 484    # Whether exp.ArraySize should generate the dimension arg too (valid for Postgres & DuckDB)
 485    # None -> Doesn't support it at all
 486    # False (DuckDB) -> Has backwards-compatible support, but preferably generated without
 487    # True (Postgres) -> Explicitly requires it
 488    ARRAY_SIZE_DIM_REQUIRED: t.Optional[bool] = None
 489
 490    # Whether a multi-argument DECODE(...) function is supported. If not, a CASE expression is generated
 491    SUPPORTS_DECODE_CASE = True
 492
 493    # Whether SYMMETRIC and ASYMMETRIC flags are supported with BETWEEN expression
 494    SUPPORTS_BETWEEN_FLAGS = False
 495
 496    # Whether LIKE and ILIKE support quantifiers such as LIKE ANY/ALL/SOME
 497    SUPPORTS_LIKE_QUANTIFIERS = True
 498
 499    TYPE_MAPPING = {
 500        exp.DataType.Type.DATETIME2: "TIMESTAMP",
 501        exp.DataType.Type.NCHAR: "CHAR",
 502        exp.DataType.Type.NVARCHAR: "VARCHAR",
 503        exp.DataType.Type.MEDIUMTEXT: "TEXT",
 504        exp.DataType.Type.LONGTEXT: "TEXT",
 505        exp.DataType.Type.TINYTEXT: "TEXT",
 506        exp.DataType.Type.BLOB: "VARBINARY",
 507        exp.DataType.Type.MEDIUMBLOB: "BLOB",
 508        exp.DataType.Type.LONGBLOB: "BLOB",
 509        exp.DataType.Type.TINYBLOB: "BLOB",
 510        exp.DataType.Type.INET: "INET",
 511        exp.DataType.Type.ROWVERSION: "VARBINARY",
 512        exp.DataType.Type.SMALLDATETIME: "TIMESTAMP",
 513    }
 514
 515    TIME_PART_SINGULARS = {
 516        "MICROSECONDS": "MICROSECOND",
 517        "SECONDS": "SECOND",
 518        "MINUTES": "MINUTE",
 519        "HOURS": "HOUR",
 520        "DAYS": "DAY",
 521        "WEEKS": "WEEK",
 522        "MONTHS": "MONTH",
 523        "QUARTERS": "QUARTER",
 524        "YEARS": "YEAR",
 525    }
 526
 527    AFTER_HAVING_MODIFIER_TRANSFORMS = {
 528        "cluster": lambda self, e: self.sql(e, "cluster"),
 529        "distribute": lambda self, e: self.sql(e, "distribute"),
 530        "sort": lambda self, e: self.sql(e, "sort"),
 531        "windows": lambda self, e: (
 532            self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True)
 533            if e.args.get("windows")
 534            else ""
 535        ),
 536        "qualify": lambda self, e: self.sql(e, "qualify"),
 537    }
 538
 539    TOKEN_MAPPING: t.Dict[TokenType, str] = {}
 540
 541    STRUCT_DELIMITER = ("<", ">")
 542
 543    PARAMETER_TOKEN = "@"
 544    NAMED_PLACEHOLDER_TOKEN = ":"
 545
 546    EXPRESSION_PRECEDES_PROPERTIES_CREATABLES: t.Set[str] = set()
 547
 548    PROPERTIES_LOCATION = {
 549        exp.AllowedValuesProperty: exp.Properties.Location.POST_SCHEMA,
 550        exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
 551        exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
 552        exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA,
 553        exp.BackupProperty: exp.Properties.Location.POST_SCHEMA,
 554        exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
 555        exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
 556        exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
 557        exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
 558        exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA,
 559        exp.Cluster: exp.Properties.Location.POST_SCHEMA,
 560        exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA,
 561        exp.DistributedByProperty: exp.Properties.Location.POST_SCHEMA,
 562        exp.DuplicateKeyProperty: exp.Properties.Location.POST_SCHEMA,
 563        exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
 564        exp.DataDeletionProperty: exp.Properties.Location.POST_SCHEMA,
 565        exp.DefinerProperty: exp.Properties.Location.POST_CREATE,
 566        exp.DictRange: exp.Properties.Location.POST_SCHEMA,
 567        exp.DictProperty: exp.Properties.Location.POST_SCHEMA,
 568        exp.DynamicProperty: exp.Properties.Location.POST_CREATE,
 569        exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA,
 570        exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA,
 571        exp.EmptyProperty: exp.Properties.Location.POST_SCHEMA,
 572        exp.EncodeProperty: exp.Properties.Location.POST_EXPRESSION,
 573        exp.EngineProperty: exp.Properties.Location.POST_SCHEMA,
 574        exp.EnviromentProperty: exp.Properties.Location.POST_SCHEMA,
 575        exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA,
 576        exp.ExternalProperty: exp.Properties.Location.POST_CREATE,
 577        exp.FallbackProperty: exp.Properties.Location.POST_NAME,
 578        exp.FileFormatProperty: exp.Properties.Location.POST_WITH,
 579        exp.FreespaceProperty: exp.Properties.Location.POST_NAME,
 580        exp.GlobalProperty: exp.Properties.Location.POST_CREATE,
 581        exp.HeapProperty: exp.Properties.Location.POST_WITH,
 582        exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA,
 583        exp.IcebergProperty: exp.Properties.Location.POST_CREATE,
 584        exp.IncludeProperty: exp.Properties.Location.POST_SCHEMA,
 585        exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA,
 586        exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME,
 587        exp.JournalProperty: exp.Properties.Location.POST_NAME,
 588        exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA,
 589        exp.LikeProperty: exp.Properties.Location.POST_SCHEMA,
 590        exp.LocationProperty: exp.Properties.Location.POST_SCHEMA,
 591        exp.LockProperty: exp.Properties.Location.POST_SCHEMA,
 592        exp.LockingProperty: exp.Properties.Location.POST_ALIAS,
 593        exp.LogProperty: exp.Properties.Location.POST_NAME,
 594        exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
 595        exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME,
 596        exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION,
 597        exp.OnProperty: exp.Properties.Location.POST_SCHEMA,
 598        exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION,
 599        exp.Order: exp.Properties.Location.POST_SCHEMA,
 600        exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA,
 601        exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
 602        exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA,
 603        exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA,
 604        exp.Property: exp.Properties.Location.POST_WITH,
 605        exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA,
 606        exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA,
 607        exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA,
 608        exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA,
 609        exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA,
 610        exp.SampleProperty: exp.Properties.Location.POST_SCHEMA,
 611        exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
 612        exp.SecureProperty: exp.Properties.Location.POST_CREATE,
 613        exp.SecurityProperty: exp.Properties.Location.POST_SCHEMA,
 614        exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
 615        exp.Set: exp.Properties.Location.POST_SCHEMA,
 616        exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA,
 617        exp.SetProperty: exp.Properties.Location.POST_CREATE,
 618        exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA,
 619        exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION,
 620        exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION,
 621        exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
 622        exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA,
 623        exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
 624        exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
 625        exp.StorageHandlerProperty: exp.Properties.Location.POST_SCHEMA,
 626        exp.StreamingTableProperty: exp.Properties.Location.POST_CREATE,
 627        exp.StrictProperty: exp.Properties.Location.POST_SCHEMA,
 628        exp.Tags: exp.Properties.Location.POST_WITH,
 629        exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
 630        exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA,
 631        exp.TransientProperty: exp.Properties.Location.POST_CREATE,
 632        exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA,
 633        exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA,
 634        exp.UnloggedProperty: exp.Properties.Location.POST_CREATE,
 635        exp.UsingTemplateProperty: exp.Properties.Location.POST_SCHEMA,
 636        exp.ViewAttributeProperty: exp.Properties.Location.POST_SCHEMA,
 637        exp.VolatileProperty: exp.Properties.Location.POST_CREATE,
 638        exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION,
 639        exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME,
 640        exp.WithProcedureOptions: exp.Properties.Location.POST_SCHEMA,
 641        exp.WithSchemaBindingProperty: exp.Properties.Location.POST_SCHEMA,
 642        exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA,
 643        exp.ForceProperty: exp.Properties.Location.POST_CREATE,
 644    }
 645
 646    # Keywords that can't be used as unquoted identifier names
 647    RESERVED_KEYWORDS: t.Set[str] = set()
 648
 649    # Expressions whose comments are separated from them for better formatting
 650    WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
 651        exp.Command,
 652        exp.Create,
 653        exp.Describe,
 654        exp.Delete,
 655        exp.Drop,
 656        exp.From,
 657        exp.Insert,
 658        exp.Join,
 659        exp.MultitableInserts,
 660        exp.Order,
 661        exp.Group,
 662        exp.Having,
 663        exp.Select,
 664        exp.SetOperation,
 665        exp.Update,
 666        exp.Where,
 667        exp.With,
 668    )
 669
 670    # Expressions that should not have their comments generated in maybe_comment
 671    EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
 672        exp.Binary,
 673        exp.SetOperation,
 674    )
 675
 676    # Expressions that can remain unwrapped when appearing in the context of an INTERVAL
 677    UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = (
 678        exp.Column,
 679        exp.Literal,
 680        exp.Neg,
 681        exp.Paren,
 682    )
 683
 684    PARAMETERIZABLE_TEXT_TYPES = {
 685        exp.DataType.Type.NVARCHAR,
 686        exp.DataType.Type.VARCHAR,
 687        exp.DataType.Type.CHAR,
 688        exp.DataType.Type.NCHAR,
 689    }
 690
 691    # Expressions that need to have all CTEs under them bubbled up to them
 692    EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set()
 693
 694    RESPECT_IGNORE_NULLS_UNSUPPORTED_EXPRESSIONS: t.Tuple[t.Type[exp.Expression], ...] = ()
 695
 696    SAFE_JSON_PATH_KEY_RE = exp.SAFE_IDENTIFIER_RE
 697
 698    SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
 699
 700    __slots__ = (
 701        "pretty",
 702        "identify",
 703        "normalize",
 704        "pad",
 705        "_indent",
 706        "normalize_functions",
 707        "unsupported_level",
 708        "max_unsupported",
 709        "leading_comma",
 710        "max_text_width",
 711        "comments",
 712        "dialect",
 713        "unsupported_messages",
 714        "_escaped_quote_end",
 715        "_escaped_identifier_end",
 716        "_next_name",
 717        "_identifier_start",
 718        "_identifier_end",
 719        "_quote_json_path_key_using_brackets",
 720    )
 721
 722    def __init__(
 723        self,
 724        pretty: t.Optional[bool] = None,
 725        identify: str | bool = False,
 726        normalize: bool = False,
 727        pad: int = 2,
 728        indent: int = 2,
 729        normalize_functions: t.Optional[str | bool] = None,
 730        unsupported_level: ErrorLevel = ErrorLevel.WARN,
 731        max_unsupported: int = 3,
 732        leading_comma: bool = False,
 733        max_text_width: int = 80,
 734        comments: bool = True,
 735        dialect: DialectType = None,
 736    ):
 737        import sqlglot
 738        from sqlglot.dialects import Dialect
 739
 740        self.pretty = pretty if pretty is not None else sqlglot.pretty
 741        self.identify = identify
 742        self.normalize = normalize
 743        self.pad = pad
 744        self._indent = indent
 745        self.unsupported_level = unsupported_level
 746        self.max_unsupported = max_unsupported
 747        self.leading_comma = leading_comma
 748        self.max_text_width = max_text_width
 749        self.comments = comments
 750        self.dialect = Dialect.get_or_raise(dialect)
 751
 752        # This is both a Dialect property and a Generator argument, so we prioritize the latter
 753        self.normalize_functions = (
 754            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
 755        )
 756
 757        self.unsupported_messages: t.List[str] = []
 758        self._escaped_quote_end: str = (
 759            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
 760        )
 761        self._escaped_identifier_end = self.dialect.IDENTIFIER_END * 2
 762
 763        self._next_name = name_sequence("_t")
 764
 765        self._identifier_start = self.dialect.IDENTIFIER_START
 766        self._identifier_end = self.dialect.IDENTIFIER_END
 767
 768        self._quote_json_path_key_using_brackets = True
 769
 770    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
 771        """
 772        Generates the SQL string corresponding to the given syntax tree.
 773
 774        Args:
 775            expression: The syntax tree.
 776            copy: Whether to copy the expression. The generator performs mutations so
 777                it is safer to copy.
 778
 779        Returns:
 780            The SQL string corresponding to `expression`.
 781        """
 782        if copy:
 783            expression = expression.copy()
 784
 785        expression = self.preprocess(expression)
 786
 787        self.unsupported_messages = []
 788        sql = self.sql(expression).strip()
 789
 790        if self.pretty:
 791            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
 792
 793        if self.unsupported_level == ErrorLevel.IGNORE:
 794            return sql
 795
 796        if self.unsupported_level == ErrorLevel.WARN:
 797            for msg in self.unsupported_messages:
 798                logger.warning(msg)
 799        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
 800            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
 801
 802        return sql
 803
 804    def preprocess(self, expression: exp.Expression) -> exp.Expression:
 805        """Apply generic preprocessing transformations to a given expression."""
 806        expression = self._move_ctes_to_top_level(expression)
 807
 808        if self.ENSURE_BOOLS:
 809            from sqlglot.transforms import ensure_bools
 810
 811            expression = ensure_bools(expression)
 812
 813        return expression
 814
 815    def _move_ctes_to_top_level(self, expression: E) -> E:
 816        if (
 817            not expression.parent
 818            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
 819            and any(node.parent is not expression for node in expression.find_all(exp.With))
 820        ):
 821            from sqlglot.transforms import move_ctes_to_top_level
 822
 823            expression = move_ctes_to_top_level(expression)
 824        return expression
 825
 826    def unsupported(self, message: str) -> None:
 827        if self.unsupported_level == ErrorLevel.IMMEDIATE:
 828            raise UnsupportedError(message)
 829        self.unsupported_messages.append(message)
 830
 831    def sep(self, sep: str = " ") -> str:
 832        return f"{sep.strip()}\n" if self.pretty else sep
 833
 834    def seg(self, sql: str, sep: str = " ") -> str:
 835        return f"{self.sep(sep)}{sql}"
 836
 837    def sanitize_comment(self, comment: str) -> str:
 838        comment = " " + comment if comment[0].strip() else comment
 839        comment = comment + " " if comment[-1].strip() else comment
 840
 841        if not self.dialect.tokenizer_class.NESTED_COMMENTS:
 842            # Necessary workaround to avoid syntax errors due to nesting: /* ... */ ... */
 843            comment = comment.replace("*/", "* /")
 844
 845        return comment
 846
 847    def maybe_comment(
 848        self,
 849        sql: str,
 850        expression: t.Optional[exp.Expression] = None,
 851        comments: t.Optional[t.List[str]] = None,
 852        separated: bool = False,
 853    ) -> str:
 854        comments = (
 855            ((expression and expression.comments) if comments is None else comments)  # type: ignore
 856            if self.comments
 857            else None
 858        )
 859
 860        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
 861            return sql
 862
 863        comments_sql = " ".join(
 864            f"/*{self.sanitize_comment(comment)}*/" for comment in comments if comment
 865        )
 866
 867        if not comments_sql:
 868            return sql
 869
 870        comments_sql = self._replace_line_breaks(comments_sql)
 871
 872        if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS):
 873            return (
 874                f"{self.sep()}{comments_sql}{sql}"
 875                if not sql or sql[0].isspace()
 876                else f"{comments_sql}{self.sep()}{sql}"
 877            )
 878
 879        return f"{sql} {comments_sql}"
 880
 881    def wrap(self, expression: exp.Expression | str) -> str:
 882        this_sql = (
 883            self.sql(expression)
 884            if isinstance(expression, exp.UNWRAPPED_QUERIES)
 885            else self.sql(expression, "this")
 886        )
 887        if not this_sql:
 888            return "()"
 889
 890        this_sql = self.indent(this_sql, level=1, pad=0)
 891        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
 892
 893    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
 894        original = self.identify
 895        self.identify = False
 896        result = func(*args, **kwargs)
 897        self.identify = original
 898        return result
 899
 900    def normalize_func(self, name: str) -> str:
 901        if self.normalize_functions == "upper" or self.normalize_functions is True:
 902            return name.upper()
 903        if self.normalize_functions == "lower":
 904            return name.lower()
 905        return name
 906
 907    def indent(
 908        self,
 909        sql: str,
 910        level: int = 0,
 911        pad: t.Optional[int] = None,
 912        skip_first: bool = False,
 913        skip_last: bool = False,
 914    ) -> str:
 915        if not self.pretty or not sql:
 916            return sql
 917
 918        pad = self.pad if pad is None else pad
 919        lines = sql.split("\n")
 920
 921        return "\n".join(
 922            (
 923                line
 924                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
 925                else f"{' ' * (level * self._indent + pad)}{line}"
 926            )
 927            for i, line in enumerate(lines)
 928        )
 929
 930    def sql(
 931        self,
 932        expression: t.Optional[str | exp.Expression],
 933        key: t.Optional[str] = None,
 934        comment: bool = True,
 935    ) -> str:
 936        if not expression:
 937            return ""
 938
 939        if isinstance(expression, str):
 940            return expression
 941
 942        if key:
 943            value = expression.args.get(key)
 944            if value:
 945                return self.sql(value)
 946            return ""
 947
 948        transform = self.TRANSFORMS.get(expression.__class__)
 949
 950        if callable(transform):
 951            sql = transform(self, expression)
 952        elif isinstance(expression, exp.Expression):
 953            exp_handler_name = f"{expression.key}_sql"
 954
 955            if hasattr(self, exp_handler_name):
 956                sql = getattr(self, exp_handler_name)(expression)
 957            elif isinstance(expression, exp.Func):
 958                sql = self.function_fallback_sql(expression)
 959            elif isinstance(expression, exp.Property):
 960                sql = self.property_sql(expression)
 961            else:
 962                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
 963        else:
 964            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
 965
 966        return self.maybe_comment(sql, expression) if self.comments and comment else sql
 967
 968    def uncache_sql(self, expression: exp.Uncache) -> str:
 969        table = self.sql(expression, "this")
 970        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
 971        return f"UNCACHE TABLE{exists_sql} {table}"
 972
 973    def cache_sql(self, expression: exp.Cache) -> str:
 974        lazy = " LAZY" if expression.args.get("lazy") else ""
 975        table = self.sql(expression, "this")
 976        options = expression.args.get("options")
 977        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
 978        sql = self.sql(expression, "expression")
 979        sql = f" AS{self.sep()}{sql}" if sql else ""
 980        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
 981        return self.prepend_ctes(expression, sql)
 982
 983    def characterset_sql(self, expression: exp.CharacterSet) -> str:
 984        if isinstance(expression.parent, exp.Cast):
 985            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
 986        default = "DEFAULT " if expression.args.get("default") else ""
 987        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
 988
 989    def column_parts(self, expression: exp.Column) -> str:
 990        return ".".join(
 991            self.sql(part)
 992            for part in (
 993                expression.args.get("catalog"),
 994                expression.args.get("db"),
 995                expression.args.get("table"),
 996                expression.args.get("this"),
 997            )
 998            if part
 999        )
1000
1001    def column_sql(self, expression: exp.Column) -> str:
1002        join_mark = " (+)" if expression.args.get("join_mark") else ""
1003
1004        if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS:
1005            join_mark = ""
1006            self.unsupported("Outer join syntax using the (+) operator is not supported.")
1007
1008        return f"{self.column_parts(expression)}{join_mark}"
1009
1010    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
1011        this = self.sql(expression, "this")
1012        this = f" {this}" if this else ""
1013        position = self.sql(expression, "position")
1014        return f"{position}{this}"
1015
1016    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
1017        column = self.sql(expression, "this")
1018        kind = self.sql(expression, "kind")
1019        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
1020        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
1021        kind = f"{sep}{kind}" if kind else ""
1022        constraints = f" {constraints}" if constraints else ""
1023        position = self.sql(expression, "position")
1024        position = f" {position}" if position else ""
1025
1026        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
1027            kind = ""
1028
1029        return f"{exists}{column}{kind}{constraints}{position}"
1030
1031    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
1032        this = self.sql(expression, "this")
1033        kind_sql = self.sql(expression, "kind").strip()
1034        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
1035
1036    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
1037        this = self.sql(expression, "this")
1038        if expression.args.get("not_null"):
1039            persisted = " PERSISTED NOT NULL"
1040        elif expression.args.get("persisted"):
1041            persisted = " PERSISTED"
1042        else:
1043            persisted = ""
1044
1045        return f"AS {this}{persisted}"
1046
1047    def autoincrementcolumnconstraint_sql(self, _) -> str:
1048        return self.token_sql(TokenType.AUTO_INCREMENT)
1049
1050    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
1051        if isinstance(expression.this, list):
1052            this = self.wrap(self.expressions(expression, key="this", flat=True))
1053        else:
1054            this = self.sql(expression, "this")
1055
1056        return f"COMPRESS {this}"
1057
1058    def generatedasidentitycolumnconstraint_sql(
1059        self, expression: exp.GeneratedAsIdentityColumnConstraint
1060    ) -> str:
1061        this = ""
1062        if expression.this is not None:
1063            on_null = " ON NULL" if expression.args.get("on_null") else ""
1064            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
1065
1066        start = expression.args.get("start")
1067        start = f"START WITH {start}" if start else ""
1068        increment = expression.args.get("increment")
1069        increment = f" INCREMENT BY {increment}" if increment else ""
1070        minvalue = expression.args.get("minvalue")
1071        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
1072        maxvalue = expression.args.get("maxvalue")
1073        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
1074        cycle = expression.args.get("cycle")
1075        cycle_sql = ""
1076
1077        if cycle is not None:
1078            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
1079            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
1080
1081        sequence_opts = ""
1082        if start or increment or cycle_sql:
1083            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
1084            sequence_opts = f" ({sequence_opts.strip()})"
1085
1086        expr = self.sql(expression, "expression")
1087        expr = f"({expr})" if expr else "IDENTITY"
1088
1089        return f"GENERATED{this} AS {expr}{sequence_opts}"
1090
1091    def generatedasrowcolumnconstraint_sql(
1092        self, expression: exp.GeneratedAsRowColumnConstraint
1093    ) -> str:
1094        start = "START" if expression.args.get("start") else "END"
1095        hidden = " HIDDEN" if expression.args.get("hidden") else ""
1096        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
1097
1098    def periodforsystemtimeconstraint_sql(
1099        self, expression: exp.PeriodForSystemTimeConstraint
1100    ) -> str:
1101        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
1102
1103    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
1104        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
1105
1106    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
1107        desc = expression.args.get("desc")
1108        if desc is not None:
1109            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
1110        options = self.expressions(expression, key="options", flat=True, sep=" ")
1111        options = f" {options}" if options else ""
1112        return f"PRIMARY KEY{options}"
1113
1114    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
1115        this = self.sql(expression, "this")
1116        this = f" {this}" if this else ""
1117        index_type = expression.args.get("index_type")
1118        index_type = f" USING {index_type}" if index_type else ""
1119        on_conflict = self.sql(expression, "on_conflict")
1120        on_conflict = f" {on_conflict}" if on_conflict else ""
1121        nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else ""
1122        options = self.expressions(expression, key="options", flat=True, sep=" ")
1123        options = f" {options}" if options else ""
1124        return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}{options}"
1125
1126    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
1127        return self.sql(expression, "this")
1128
1129    def create_sql(self, expression: exp.Create) -> str:
1130        kind = self.sql(expression, "kind")
1131        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
1132        properties = expression.args.get("properties")
1133        properties_locs = self.locate_properties(properties) if properties else defaultdict()
1134
1135        this = self.createable_sql(expression, properties_locs)
1136
1137        properties_sql = ""
1138        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
1139            exp.Properties.Location.POST_WITH
1140        ):
1141            props_ast = exp.Properties(
1142                expressions=[
1143                    *properties_locs[exp.Properties.Location.POST_SCHEMA],
1144                    *properties_locs[exp.Properties.Location.POST_WITH],
1145                ]
1146            )
1147            props_ast.parent = expression
1148            properties_sql = self.sql(props_ast)
1149
1150            if properties_locs.get(exp.Properties.Location.POST_SCHEMA):
1151                properties_sql = self.sep() + properties_sql
1152            elif not self.pretty:
1153                # Standalone POST_WITH properties need a leading whitespace in non-pretty mode
1154                properties_sql = f" {properties_sql}"
1155
1156        begin = " BEGIN" if expression.args.get("begin") else ""
1157        end = " END" if expression.args.get("end") else ""
1158
1159        expression_sql = self.sql(expression, "expression")
1160        if expression_sql:
1161            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
1162
1163            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
1164                postalias_props_sql = ""
1165                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
1166                    postalias_props_sql = self.properties(
1167                        exp.Properties(
1168                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
1169                        ),
1170                        wrapped=False,
1171                    )
1172                postalias_props_sql = f" {postalias_props_sql}" if postalias_props_sql else ""
1173                expression_sql = f" AS{postalias_props_sql}{expression_sql}"
1174
1175        postindex_props_sql = ""
1176        if properties_locs.get(exp.Properties.Location.POST_INDEX):
1177            postindex_props_sql = self.properties(
1178                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
1179                wrapped=False,
1180                prefix=" ",
1181            )
1182
1183        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
1184        indexes = f" {indexes}" if indexes else ""
1185        index_sql = indexes + postindex_props_sql
1186
1187        replace = " OR REPLACE" if expression.args.get("replace") else ""
1188        refresh = " OR REFRESH" if expression.args.get("refresh") else ""
1189        unique = " UNIQUE" if expression.args.get("unique") else ""
1190
1191        clustered = expression.args.get("clustered")
1192        if clustered is None:
1193            clustered_sql = ""
1194        elif clustered:
1195            clustered_sql = " CLUSTERED COLUMNSTORE"
1196        else:
1197            clustered_sql = " NONCLUSTERED COLUMNSTORE"
1198
1199        postcreate_props_sql = ""
1200        if properties_locs.get(exp.Properties.Location.POST_CREATE):
1201            postcreate_props_sql = self.properties(
1202                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
1203                sep=" ",
1204                prefix=" ",
1205                wrapped=False,
1206            )
1207
1208        modifiers = "".join((clustered_sql, replace, refresh, unique, postcreate_props_sql))
1209
1210        postexpression_props_sql = ""
1211        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
1212            postexpression_props_sql = self.properties(
1213                exp.Properties(
1214                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
1215                ),
1216                sep=" ",
1217                prefix=" ",
1218                wrapped=False,
1219            )
1220
1221        concurrently = " CONCURRENTLY" if expression.args.get("concurrently") else ""
1222        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
1223        no_schema_binding = (
1224            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
1225        )
1226
1227        clone = self.sql(expression, "clone")
1228        clone = f" {clone}" if clone else ""
1229
1230        if kind in self.EXPRESSION_PRECEDES_PROPERTIES_CREATABLES:
1231            properties_expression = f"{expression_sql}{properties_sql}"
1232        else:
1233            properties_expression = f"{properties_sql}{expression_sql}"
1234
1235        expression_sql = f"CREATE{modifiers} {kind}{concurrently}{exists_sql} {this}{properties_expression}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
1236        return self.prepend_ctes(expression, expression_sql)
1237
1238    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
1239        start = self.sql(expression, "start")
1240        start = f"START WITH {start}" if start else ""
1241        increment = self.sql(expression, "increment")
1242        increment = f" INCREMENT BY {increment}" if increment else ""
1243        minvalue = self.sql(expression, "minvalue")
1244        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
1245        maxvalue = self.sql(expression, "maxvalue")
1246        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
1247        owned = self.sql(expression, "owned")
1248        owned = f" OWNED BY {owned}" if owned else ""
1249
1250        cache = expression.args.get("cache")
1251        if cache is None:
1252            cache_str = ""
1253        elif cache is True:
1254            cache_str = " CACHE"
1255        else:
1256            cache_str = f" CACHE {cache}"
1257
1258        options = self.expressions(expression, key="options", flat=True, sep=" ")
1259        options = f" {options}" if options else ""
1260
1261        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()
1262
1263    def clone_sql(self, expression: exp.Clone) -> str:
1264        this = self.sql(expression, "this")
1265        shallow = "SHALLOW " if expression.args.get("shallow") else ""
1266        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
1267        return f"{shallow}{keyword} {this}"
1268
1269    def describe_sql(self, expression: exp.Describe) -> str:
1270        style = expression.args.get("style")
1271        style = f" {style}" if style else ""
1272        partition = self.sql(expression, "partition")
1273        partition = f" {partition}" if partition else ""
1274        format = self.sql(expression, "format")
1275        format = f" {format}" if format else ""
1276
1277        return f"DESCRIBE{style}{format} {self.sql(expression, 'this')}{partition}"
1278
1279    def heredoc_sql(self, expression: exp.Heredoc) -> str:
1280        tag = self.sql(expression, "tag")
1281        return f"${tag}${self.sql(expression, 'this')}${tag}$"
1282
1283    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
1284        with_ = self.sql(expression, "with")
1285        if with_:
1286            sql = f"{with_}{self.sep()}{sql}"
1287        return sql
1288
1289    def with_sql(self, expression: exp.With) -> str:
1290        sql = self.expressions(expression, flat=True)
1291        recursive = (
1292            "RECURSIVE "
1293            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
1294            else ""
1295        )
1296        search = self.sql(expression, "search")
1297        search = f" {search}" if search else ""
1298
1299        return f"WITH {recursive}{sql}{search}"
1300
1301    def cte_sql(self, expression: exp.CTE) -> str:
1302        alias = expression.args.get("alias")
1303        if alias:
1304            alias.add_comments(expression.pop_comments())
1305
1306        alias_sql = self.sql(expression, "alias")
1307
1308        materialized = expression.args.get("materialized")
1309        if materialized is False:
1310            materialized = "NOT MATERIALIZED "
1311        elif materialized:
1312            materialized = "MATERIALIZED "
1313
1314        return f"{alias_sql} AS {materialized or ''}{self.wrap(expression)}"
1315
1316    def tablealias_sql(self, expression: exp.TableAlias) -> str:
1317        alias = self.sql(expression, "this")
1318        columns = self.expressions(expression, key="columns", flat=True)
1319        columns = f"({columns})" if columns else ""
1320
1321        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
1322            columns = ""
1323            self.unsupported("Named columns are not supported in table alias.")
1324
1325        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
1326            alias = self._next_name()
1327
1328        return f"{alias}{columns}"
1329
1330    def bitstring_sql(self, expression: exp.BitString) -> str:
1331        this = self.sql(expression, "this")
1332        if self.dialect.BIT_START:
1333            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
1334        return f"{int(this, 2)}"
1335
1336    def hexstring_sql(
1337        self, expression: exp.HexString, binary_function_repr: t.Optional[str] = None
1338    ) -> str:
1339        this = self.sql(expression, "this")
1340        is_integer_type = expression.args.get("is_integer")
1341
1342        if (is_integer_type and not self.dialect.HEX_STRING_IS_INTEGER_TYPE) or (
1343            not self.dialect.HEX_START and not binary_function_repr
1344        ):
1345            # Integer representation will be returned if:
1346            # - The read dialect treats the hex value as integer literal but not the write
1347            # - The transpilation is not supported (write dialect hasn't set HEX_START or the param flag)
1348            return f"{int(this, 16)}"
1349
1350        if not is_integer_type:
1351            # Read dialect treats the hex value as BINARY/BLOB
1352            if binary_function_repr:
1353                # The write dialect supports the transpilation to its equivalent BINARY/BLOB
1354                return self.func(binary_function_repr, exp.Literal.string(this))
1355            if self.dialect.HEX_STRING_IS_INTEGER_TYPE:
1356                # The write dialect does not support the transpilation, it'll treat the hex value as INTEGER
1357                self.unsupported("Unsupported transpilation from BINARY/BLOB hex string")
1358
1359        return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
1360
1361    def bytestring_sql(self, expression: exp.ByteString) -> str:
1362        this = self.sql(expression, "this")
1363        if self.dialect.BYTE_START:
1364            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
1365        return this
1366
1367    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
1368        this = self.sql(expression, "this")
1369        escape = expression.args.get("escape")
1370
1371        if self.dialect.UNICODE_START:
1372            escape_substitute = r"\\\1"
1373            left_quote, right_quote = self.dialect.UNICODE_START, self.dialect.UNICODE_END
1374        else:
1375            escape_substitute = r"\\u\1"
1376            left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END
1377
1378        if escape:
1379            escape_pattern = re.compile(rf"{escape.name}(\d+)")
1380            escape_sql = f" UESCAPE {self.sql(escape)}" if self.SUPPORTS_UESCAPE else ""
1381        else:
1382            escape_pattern = ESCAPED_UNICODE_RE
1383            escape_sql = ""
1384
1385        if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE):
1386            this = escape_pattern.sub(escape_substitute, this)
1387
1388        return f"{left_quote}{this}{right_quote}{escape_sql}"
1389
1390    def rawstring_sql(self, expression: exp.RawString) -> str:
1391        string = expression.this
1392        if "\\" in self.dialect.tokenizer_class.STRING_ESCAPES:
1393            string = string.replace("\\", "\\\\")
1394
1395        string = self.escape_str(string, escape_backslash=False)
1396        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
1397
1398    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
1399        this = self.sql(expression, "this")
1400        specifier = self.sql(expression, "expression")
1401        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
1402        return f"{this}{specifier}"
1403
1404    def datatype_sql(self, expression: exp.DataType) -> str:
1405        nested = ""
1406        values = ""
1407        interior = self.expressions(expression, flat=True)
1408
1409        type_value = expression.this
1410        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
1411            type_sql = self.sql(expression, "kind")
1412        else:
1413            type_sql = (
1414                self.TYPE_MAPPING.get(type_value, type_value.value)
1415                if isinstance(type_value, exp.DataType.Type)
1416                else type_value
1417            )
1418
1419        if interior:
1420            if expression.args.get("nested"):
1421                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
1422                if expression.args.get("values") is not None:
1423                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
1424                    values = self.expressions(expression, key="values", flat=True)
1425                    values = f"{delimiters[0]}{values}{delimiters[1]}"
1426            elif type_value == exp.DataType.Type.INTERVAL:
1427                nested = f" {interior}"
1428            else:
1429                nested = f"({interior})"
1430
1431        type_sql = f"{type_sql}{nested}{values}"
1432        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
1433            exp.DataType.Type.TIMETZ,
1434            exp.DataType.Type.TIMESTAMPTZ,
1435        ):
1436            type_sql = f"{type_sql} WITH TIME ZONE"
1437
1438        return type_sql
1439
1440    def directory_sql(self, expression: exp.Directory) -> str:
1441        local = "LOCAL " if expression.args.get("local") else ""
1442        row_format = self.sql(expression, "row_format")
1443        row_format = f" {row_format}" if row_format else ""
1444        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
1445
1446    def delete_sql(self, expression: exp.Delete) -> str:
1447        this = self.sql(expression, "this")
1448        this = f" FROM {this}" if this else ""
1449        using = self.sql(expression, "using")
1450        using = f" USING {using}" if using else ""
1451        cluster = self.sql(expression, "cluster")
1452        cluster = f" {cluster}" if cluster else ""
1453        where = self.sql(expression, "where")
1454        returning = self.sql(expression, "returning")
1455        limit = self.sql(expression, "limit")
1456        tables = self.expressions(expression, key="tables")
1457        tables = f" {tables}" if tables else ""
1458        if self.RETURNING_END:
1459            expression_sql = f"{this}{using}{cluster}{where}{returning}{limit}"
1460        else:
1461            expression_sql = f"{returning}{this}{using}{cluster}{where}{limit}"
1462        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
1463
1464    def drop_sql(self, expression: exp.Drop) -> str:
1465        this = self.sql(expression, "this")
1466        expressions = self.expressions(expression, flat=True)
1467        expressions = f" ({expressions})" if expressions else ""
1468        kind = expression.args["kind"]
1469        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
1470        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
1471        concurrently_sql = " CONCURRENTLY" if expression.args.get("concurrently") else ""
1472        on_cluster = self.sql(expression, "cluster")
1473        on_cluster = f" {on_cluster}" if on_cluster else ""
1474        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1475        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
1476        cascade = " CASCADE" if expression.args.get("cascade") else ""
1477        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
1478        purge = " PURGE" if expression.args.get("purge") else ""
1479        return f"DROP{temporary}{materialized} {kind}{concurrently_sql}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"
1480
1481    def set_operation(self, expression: exp.SetOperation) -> str:
1482        op_type = type(expression)
1483        op_name = op_type.key.upper()
1484
1485        distinct = expression.args.get("distinct")
1486        if (
1487            distinct is False
1488            and op_type in (exp.Except, exp.Intersect)
1489            and not self.EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE
1490        ):
1491            self.unsupported(f"{op_name} ALL is not supported")
1492
1493        default_distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[op_type]
1494
1495        if distinct is None:
1496            distinct = default_distinct
1497            if distinct is None:
1498                self.unsupported(f"{op_name} requires DISTINCT or ALL to be specified")
1499
1500        if distinct is default_distinct:
1501            distinct_or_all = ""
1502        else:
1503            distinct_or_all = " DISTINCT" if distinct else " ALL"
1504
1505        side_kind = " ".join(filter(None, [expression.side, expression.kind]))
1506        side_kind = f"{side_kind} " if side_kind else ""
1507
1508        by_name = " BY NAME" if expression.args.get("by_name") else ""
1509        on = self.expressions(expression, key="on", flat=True)
1510        on = f" ON ({on})" if on else ""
1511
1512        return f"{side_kind}{op_name}{distinct_or_all}{by_name}{on}"
1513
1514    def set_operations(self, expression: exp.SetOperation) -> str:
1515        if not self.SET_OP_MODIFIERS:
1516            limit = expression.args.get("limit")
1517            order = expression.args.get("order")
1518
1519            if limit or order:
1520                select = self._move_ctes_to_top_level(
1521                    exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
1522                )
1523
1524                if limit:
1525                    select = select.limit(limit.pop(), copy=False)
1526                if order:
1527                    select = select.order_by(order.pop(), copy=False)
1528                return self.sql(select)
1529
1530        sqls: t.List[str] = []
1531        stack: t.List[t.Union[str, exp.Expression]] = [expression]
1532
1533        while stack:
1534            node = stack.pop()
1535
1536            if isinstance(node, exp.SetOperation):
1537                stack.append(node.expression)
1538                stack.append(
1539                    self.maybe_comment(
1540                        self.set_operation(node), comments=node.comments, separated=True
1541                    )
1542                )
1543                stack.append(node.this)
1544            else:
1545                sqls.append(self.sql(node))
1546
1547        this = self.sep().join(sqls)
1548        this = self.query_modifiers(expression, this)
1549        return self.prepend_ctes(expression, this)
1550
1551    def fetch_sql(self, expression: exp.Fetch) -> str:
1552        direction = expression.args.get("direction")
1553        direction = f" {direction}" if direction else ""
1554        count = self.sql(expression, "count")
1555        count = f" {count}" if count else ""
1556        limit_options = self.sql(expression, "limit_options")
1557        limit_options = f"{limit_options}" if limit_options else " ROWS ONLY"
1558        return f"{self.seg('FETCH')}{direction}{count}{limit_options}"
1559
1560    def limitoptions_sql(self, expression: exp.LimitOptions) -> str:
1561        percent = " PERCENT" if expression.args.get("percent") else ""
1562        rows = " ROWS" if expression.args.get("rows") else ""
1563        with_ties = " WITH TIES" if expression.args.get("with_ties") else ""
1564        if not with_ties and rows:
1565            with_ties = " ONLY"
1566        return f"{percent}{rows}{with_ties}"
1567
1568    def filter_sql(self, expression: exp.Filter) -> str:
1569        if self.AGGREGATE_FILTER_SUPPORTED:
1570            this = self.sql(expression, "this")
1571            where = self.sql(expression, "expression").strip()
1572            return f"{this} FILTER({where})"
1573
1574        agg = expression.this
1575        agg_arg = agg.this
1576        cond = expression.expression.this
1577        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
1578        return self.sql(agg)
1579
1580    def hint_sql(self, expression: exp.Hint) -> str:
1581        if not self.QUERY_HINTS:
1582            self.unsupported("Hints are not supported")
1583            return ""
1584
1585        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
1586
1587    def indexparameters_sql(self, expression: exp.IndexParameters) -> str:
1588        using = self.sql(expression, "using")
1589        using = f" USING {using}" if using else ""
1590        columns = self.expressions(expression, key="columns", flat=True)
1591        columns = f"({columns})" if columns else ""
1592        partition_by = self.expressions(expression, key="partition_by", flat=True)
1593        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
1594        where = self.sql(expression, "where")
1595        include = self.expressions(expression, key="include", flat=True)
1596        if include:
1597            include = f" INCLUDE ({include})"
1598        with_storage = self.expressions(expression, key="with_storage", flat=True)
1599        with_storage = f" WITH ({with_storage})" if with_storage else ""
1600        tablespace = self.sql(expression, "tablespace")
1601        tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
1602        on = self.sql(expression, "on")
1603        on = f" ON {on}" if on else ""
1604
1605        return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}"
1606
1607    def index_sql(self, expression: exp.Index) -> str:
1608        unique = "UNIQUE " if expression.args.get("unique") else ""
1609        primary = "PRIMARY " if expression.args.get("primary") else ""
1610        amp = "AMP " if expression.args.get("amp") else ""
1611        name = self.sql(expression, "this")
1612        name = f"{name} " if name else ""
1613        table = self.sql(expression, "table")
1614        table = f"{self.INDEX_ON} {table}" if table else ""
1615
1616        index = "INDEX " if not table else ""
1617
1618        params = self.sql(expression, "params")
1619        return f"{unique}{primary}{amp}{index}{name}{table}{params}"
1620
1621    def identifier_sql(self, expression: exp.Identifier) -> str:
1622        text = expression.name
1623        lower = text.lower()
1624        text = lower if self.normalize and not expression.quoted else text
1625        text = text.replace(self._identifier_end, self._escaped_identifier_end)
1626        if (
1627            expression.quoted
1628            or self.dialect.can_identify(text, self.identify)
1629            or lower in self.RESERVED_KEYWORDS
1630            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
1631        ):
1632            text = f"{self._identifier_start}{text}{self._identifier_end}"
1633        return text
1634
1635    def hex_sql(self, expression: exp.Hex) -> str:
1636        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
1637        if self.dialect.HEX_LOWERCASE:
1638            text = self.func("LOWER", text)
1639
1640        return text
1641
1642    def lowerhex_sql(self, expression: exp.LowerHex) -> str:
1643        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
1644        if not self.dialect.HEX_LOWERCASE:
1645            text = self.func("LOWER", text)
1646        return text
1647
1648    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
1649        input_format = self.sql(expression, "input_format")
1650        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
1651        output_format = self.sql(expression, "output_format")
1652        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
1653        return self.sep().join((input_format, output_format))
1654
1655    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
1656        string = self.sql(exp.Literal.string(expression.name))
1657        return f"{prefix}{string}"
1658
1659    def partition_sql(self, expression: exp.Partition) -> str:
1660        partition_keyword = "SUBPARTITION" if expression.args.get("subpartition") else "PARTITION"
1661        return f"{partition_keyword}({self.expressions(expression, flat=True)})"
1662
1663    def properties_sql(self, expression: exp.Properties) -> str:
1664        root_properties = []
1665        with_properties = []
1666
1667        for p in expression.expressions:
1668            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1669            if p_loc == exp.Properties.Location.POST_WITH:
1670                with_properties.append(p)
1671            elif p_loc == exp.Properties.Location.POST_SCHEMA:
1672                root_properties.append(p)
1673
1674        root_props_ast = exp.Properties(expressions=root_properties)
1675        root_props_ast.parent = expression.parent
1676
1677        with_props_ast = exp.Properties(expressions=with_properties)
1678        with_props_ast.parent = expression.parent
1679
1680        root_props = self.root_properties(root_props_ast)
1681        with_props = self.with_properties(with_props_ast)
1682
1683        if root_props and with_props and not self.pretty:
1684            with_props = " " + with_props
1685
1686        return root_props + with_props
1687
1688    def root_properties(self, properties: exp.Properties) -> str:
1689        if properties.expressions:
1690            return self.expressions(properties, indent=False, sep=" ")
1691        return ""
1692
1693    def properties(
1694        self,
1695        properties: exp.Properties,
1696        prefix: str = "",
1697        sep: str = ", ",
1698        suffix: str = "",
1699        wrapped: bool = True,
1700    ) -> str:
1701        if properties.expressions:
1702            expressions = self.expressions(properties, sep=sep, indent=False)
1703            if expressions:
1704                expressions = self.wrap(expressions) if wrapped else expressions
1705                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
1706        return ""
1707
1708    def with_properties(self, properties: exp.Properties) -> str:
1709        return self.properties(properties, prefix=self.seg(self.WITH_PROPERTIES_PREFIX, sep=""))
1710
1711    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
1712        properties_locs = defaultdict(list)
1713        for p in properties.expressions:
1714            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1715            if p_loc != exp.Properties.Location.UNSUPPORTED:
1716                properties_locs[p_loc].append(p)
1717            else:
1718                self.unsupported(f"Unsupported property {p.key}")
1719
1720        return properties_locs
1721
1722    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
1723        if isinstance(expression.this, exp.Dot):
1724            return self.sql(expression, "this")
1725        return f"'{expression.name}'" if string_key else expression.name
1726
1727    def property_sql(self, expression: exp.Property) -> str:
1728        property_cls = expression.__class__
1729        if property_cls == exp.Property:
1730            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
1731
1732        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
1733        if not property_name:
1734            self.unsupported(f"Unsupported property {expression.key}")
1735
1736        return f"{property_name}={self.sql(expression, 'this')}"
1737
1738    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
1739        if self.SUPPORTS_CREATE_TABLE_LIKE:
1740            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
1741            options = f" {options}" if options else ""
1742
1743            like = f"LIKE {self.sql(expression, 'this')}{options}"
1744            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
1745                like = f"({like})"
1746
1747            return like
1748
1749        if expression.expressions:
1750            self.unsupported("Transpilation of LIKE property options is unsupported")
1751
1752        select = exp.select("*").from_(expression.this).limit(0)
1753        return f"AS {self.sql(select)}"
1754
1755    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
1756        no = "NO " if expression.args.get("no") else ""
1757        protection = " PROTECTION" if expression.args.get("protection") else ""
1758        return f"{no}FALLBACK{protection}"
1759
1760    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
1761        no = "NO " if expression.args.get("no") else ""
1762        local = expression.args.get("local")
1763        local = f"{local} " if local else ""
1764        dual = "DUAL " if expression.args.get("dual") else ""
1765        before = "BEFORE " if expression.args.get("before") else ""
1766        after = "AFTER " if expression.args.get("after") else ""
1767        return f"{no}{local}{dual}{before}{after}JOURNAL"
1768
1769    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
1770        freespace = self.sql(expression, "this")
1771        percent = " PERCENT" if expression.args.get("percent") else ""
1772        return f"FREESPACE={freespace}{percent}"
1773
1774    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
1775        if expression.args.get("default"):
1776            property = "DEFAULT"
1777        elif expression.args.get("on"):
1778            property = "ON"
1779        else:
1780            property = "OFF"
1781        return f"CHECKSUM={property}"
1782
1783    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
1784        if expression.args.get("no"):
1785            return "NO MERGEBLOCKRATIO"
1786        if expression.args.get("default"):
1787            return "DEFAULT MERGEBLOCKRATIO"
1788
1789        percent = " PERCENT" if expression.args.get("percent") else ""
1790        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
1791
1792    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
1793        default = expression.args.get("default")
1794        minimum = expression.args.get("minimum")
1795        maximum = expression.args.get("maximum")
1796        if default or minimum or maximum:
1797            if default:
1798                prop = "DEFAULT"
1799            elif minimum:
1800                prop = "MINIMUM"
1801            else:
1802                prop = "MAXIMUM"
1803            return f"{prop} DATABLOCKSIZE"
1804        units = expression.args.get("units")
1805        units = f" {units}" if units else ""
1806        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
1807
1808    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
1809        autotemp = expression.args.get("autotemp")
1810        always = expression.args.get("always")
1811        default = expression.args.get("default")
1812        manual = expression.args.get("manual")
1813        never = expression.args.get("never")
1814
1815        if autotemp is not None:
1816            prop = f"AUTOTEMP({self.expressions(autotemp)})"
1817        elif always:
1818            prop = "ALWAYS"
1819        elif default:
1820            prop = "DEFAULT"
1821        elif manual:
1822            prop = "MANUAL"
1823        elif never:
1824            prop = "NEVER"
1825        return f"BLOCKCOMPRESSION={prop}"
1826
1827    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
1828        no = expression.args.get("no")
1829        no = " NO" if no else ""
1830        concurrent = expression.args.get("concurrent")
1831        concurrent = " CONCURRENT" if concurrent else ""
1832        target = self.sql(expression, "target")
1833        target = f" {target}" if target else ""
1834        return f"WITH{no}{concurrent} ISOLATED LOADING{target}"
1835
1836    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
1837        if isinstance(expression.this, list):
1838            return f"IN ({self.expressions(expression, key='this', flat=True)})"
1839        if expression.this:
1840            modulus = self.sql(expression, "this")
1841            remainder = self.sql(expression, "expression")
1842            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
1843
1844        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
1845        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
1846        return f"FROM ({from_expressions}) TO ({to_expressions})"
1847
1848    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
1849        this = self.sql(expression, "this")
1850
1851        for_values_or_default = expression.expression
1852        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
1853            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
1854        else:
1855            for_values_or_default = " DEFAULT"
1856
1857        return f"PARTITION OF {this}{for_values_or_default}"
1858
1859    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
1860        kind = expression.args.get("kind")
1861        this = f" {self.sql(expression, 'this')}" if expression.this else ""
1862        for_or_in = expression.args.get("for_or_in")
1863        for_or_in = f" {for_or_in}" if for_or_in else ""
1864        lock_type = expression.args.get("lock_type")
1865        override = " OVERRIDE" if expression.args.get("override") else ""
1866        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
1867
1868    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
1869        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
1870        statistics = expression.args.get("statistics")
1871        statistics_sql = ""
1872        if statistics is not None:
1873            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
1874        return f"{data_sql}{statistics_sql}"
1875
1876    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
1877        this = self.sql(expression, "this")
1878        this = f"HISTORY_TABLE={this}" if this else ""
1879        data_consistency: t.Optional[str] = self.sql(expression, "data_consistency")
1880        data_consistency = (
1881            f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None
1882        )
1883        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
1884        retention_period = (
1885            f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None
1886        )
1887
1888        if this:
1889            on_sql = self.func("ON", this, data_consistency, retention_period)
1890        else:
1891            on_sql = "ON" if expression.args.get("on") else "OFF"
1892
1893        sql = f"SYSTEM_VERSIONING={on_sql}"
1894
1895        return f"WITH({sql})" if expression.args.get("with") else sql
1896
1897    def insert_sql(self, expression: exp.Insert) -> str:
1898        hint = self.sql(expression, "hint")
1899        overwrite = expression.args.get("overwrite")
1900
1901        if isinstance(expression.this, exp.Directory):
1902            this = " OVERWRITE" if overwrite else " INTO"
1903        else:
1904            this = self.INSERT_OVERWRITE if overwrite else " INTO"
1905
1906        stored = self.sql(expression, "stored")
1907        stored = f" {stored}" if stored else ""
1908        alternative = expression.args.get("alternative")
1909        alternative = f" OR {alternative}" if alternative else ""
1910        ignore = " IGNORE" if expression.args.get("ignore") else ""
1911        is_function = expression.args.get("is_function")
1912        if is_function:
1913            this = f"{this} FUNCTION"
1914        this = f"{this} {self.sql(expression, 'this')}"
1915
1916        exists = " IF EXISTS" if expression.args.get("exists") else ""
1917        where = self.sql(expression, "where")
1918        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
1919        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
1920        on_conflict = self.sql(expression, "conflict")
1921        on_conflict = f" {on_conflict}" if on_conflict else ""
1922        by_name = " BY NAME" if expression.args.get("by_name") else ""
1923        returning = self.sql(expression, "returning")
1924
1925        if self.RETURNING_END:
1926            expression_sql = f"{expression_sql}{on_conflict}{returning}"
1927        else:
1928            expression_sql = f"{returning}{expression_sql}{on_conflict}"
1929
1930        partition_by = self.sql(expression, "partition")
1931        partition_by = f" {partition_by}" if partition_by else ""
1932        settings = self.sql(expression, "settings")
1933        settings = f" {settings}" if settings else ""
1934
1935        source = self.sql(expression, "source")
1936        source = f"TABLE {source}" if source else ""
1937
1938        sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{partition_by}{settings}{where}{expression_sql}{source}"
1939        return self.prepend_ctes(expression, sql)
1940
1941    def introducer_sql(self, expression: exp.Introducer) -> str:
1942        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
1943
1944    def kill_sql(self, expression: exp.Kill) -> str:
1945        kind = self.sql(expression, "kind")
1946        kind = f" {kind}" if kind else ""
1947        this = self.sql(expression, "this")
1948        this = f" {this}" if this else ""
1949        return f"KILL{kind}{this}"
1950
1951    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
1952        return expression.name
1953
1954    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
1955        return expression.name
1956
1957    def onconflict_sql(self, expression: exp.OnConflict) -> str:
1958        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
1959
1960        constraint = self.sql(expression, "constraint")
1961        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
1962
1963        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
1964        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
1965        action = self.sql(expression, "action")
1966
1967        expressions = self.expressions(expression, flat=True)
1968        if expressions:
1969            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
1970            expressions = f" {set_keyword}{expressions}"
1971
1972        where = self.sql(expression, "where")
1973        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}{where}"
1974
1975    def returning_sql(self, expression: exp.Returning) -> str:
1976        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
1977
1978    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
1979        fields = self.sql(expression, "fields")
1980        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
1981        escaped = self.sql(expression, "escaped")
1982        escaped = f" ESCAPED BY {escaped}" if escaped else ""
1983        items = self.sql(expression, "collection_items")
1984        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
1985        keys = self.sql(expression, "map_keys")
1986        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
1987        lines = self.sql(expression, "lines")
1988        lines = f" LINES TERMINATED BY {lines}" if lines else ""
1989        null = self.sql(expression, "null")
1990        null = f" NULL DEFINED AS {null}" if null else ""
1991        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
1992
1993    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
1994        return f"WITH ({self.expressions(expression, flat=True)})"
1995
1996    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
1997        this = f"{self.sql(expression, 'this')} INDEX"
1998        target = self.sql(expression, "target")
1999        target = f" FOR {target}" if target else ""
2000        return f"{this}{target} ({self.expressions(expression, flat=True)})"
2001
2002    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
2003        this = self.sql(expression, "this")
2004        kind = self.sql(expression, "kind")
2005        expr = self.sql(expression, "expression")
2006        return f"{this} ({kind} => {expr})"
2007
2008    def table_parts(self, expression: exp.Table) -> str:
2009        return ".".join(
2010            self.sql(part)
2011            for part in (
2012                expression.args.get("catalog"),
2013                expression.args.get("db"),
2014                expression.args.get("this"),
2015            )
2016            if part is not None
2017        )
2018
2019    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
2020        table = self.table_parts(expression)
2021        only = "ONLY " if expression.args.get("only") else ""
2022        partition = self.sql(expression, "partition")
2023        partition = f" {partition}" if partition else ""
2024        version = self.sql(expression, "version")
2025        version = f" {version}" if version else ""
2026        alias = self.sql(expression, "alias")
2027        alias = f"{sep}{alias}" if alias else ""
2028
2029        sample = self.sql(expression, "sample")
2030        if self.dialect.ALIAS_POST_TABLESAMPLE:
2031            sample_pre_alias = sample
2032            sample_post_alias = ""
2033        else:
2034            sample_pre_alias = ""
2035            sample_post_alias = sample
2036
2037        hints = self.expressions(expression, key="hints", sep=" ")
2038        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
2039        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2040        joins = self.indent(
2041            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
2042        )
2043        laterals = self.expressions(expression, key="laterals", sep="")
2044
2045        file_format = self.sql(expression, "format")
2046        if file_format:
2047            pattern = self.sql(expression, "pattern")
2048            pattern = f", PATTERN => {pattern}" if pattern else ""
2049            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
2050
2051        ordinality = expression.args.get("ordinality") or ""
2052        if ordinality:
2053            ordinality = f" WITH ORDINALITY{alias}"
2054            alias = ""
2055
2056        when = self.sql(expression, "when")
2057        if when:
2058            table = f"{table} {when}"
2059
2060        changes = self.sql(expression, "changes")
2061        changes = f" {changes}" if changes else ""
2062
2063        rows_from = self.expressions(expression, key="rows_from")
2064        if rows_from:
2065            table = f"ROWS FROM {self.wrap(rows_from)}"
2066
2067        return f"{only}{table}{changes}{partition}{version}{file_format}{sample_pre_alias}{alias}{hints}{pivots}{sample_post_alias}{joins}{laterals}{ordinality}"
2068
2069    def tablefromrows_sql(self, expression: exp.TableFromRows) -> str:
2070        table = self.func("TABLE", expression.this)
2071        alias = self.sql(expression, "alias")
2072        alias = f" AS {alias}" if alias else ""
2073        sample = self.sql(expression, "sample")
2074        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2075        joins = self.indent(
2076            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
2077        )
2078        return f"{table}{alias}{pivots}{sample}{joins}"
2079
2080    def tablesample_sql(
2081        self,
2082        expression: exp.TableSample,
2083        tablesample_keyword: t.Optional[str] = None,
2084    ) -> str:
2085        method = self.sql(expression, "method")
2086        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
2087        numerator = self.sql(expression, "bucket_numerator")
2088        denominator = self.sql(expression, "bucket_denominator")
2089        field = self.sql(expression, "bucket_field")
2090        field = f" ON {field}" if field else ""
2091        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
2092        seed = self.sql(expression, "seed")
2093        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
2094
2095        size = self.sql(expression, "size")
2096        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
2097            size = f"{size} ROWS"
2098
2099        percent = self.sql(expression, "percent")
2100        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
2101            percent = f"{percent} PERCENT"
2102
2103        expr = f"{bucket}{percent}{size}"
2104        if self.TABLESAMPLE_REQUIRES_PARENS:
2105            expr = f"({expr})"
2106
2107        return f" {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}"
2108
2109    def pivot_sql(self, expression: exp.Pivot) -> str:
2110        expressions = self.expressions(expression, flat=True)
2111        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
2112
2113        group = self.sql(expression, "group")
2114
2115        if expression.this:
2116            this = self.sql(expression, "this")
2117            if not expressions:
2118                return f"UNPIVOT {this}"
2119
2120            on = f"{self.seg('ON')} {expressions}"
2121            into = self.sql(expression, "into")
2122            into = f"{self.seg('INTO')} {into}" if into else ""
2123            using = self.expressions(expression, key="using", flat=True)
2124            using = f"{self.seg('USING')} {using}" if using else ""
2125            return f"{direction} {this}{on}{into}{using}{group}"
2126
2127        alias = self.sql(expression, "alias")
2128        alias = f" AS {alias}" if alias else ""
2129
2130        fields = self.expressions(
2131            expression,
2132            "fields",
2133            sep=" ",
2134            dynamic=True,
2135            new_line=True,
2136            skip_first=True,
2137            skip_last=True,
2138        )
2139
2140        include_nulls = expression.args.get("include_nulls")
2141        if include_nulls is not None:
2142            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
2143        else:
2144            nulls = ""
2145
2146        default_on_null = self.sql(expression, "default_on_null")
2147        default_on_null = f" DEFAULT ON NULL ({default_on_null})" if default_on_null else ""
2148        return f"{self.seg(direction)}{nulls}({expressions} FOR {fields}{default_on_null}{group}){alias}"
2149
2150    def version_sql(self, expression: exp.Version) -> str:
2151        this = f"FOR {expression.name}"
2152        kind = expression.text("kind")
2153        expr = self.sql(expression, "expression")
2154        return f"{this} {kind} {expr}"
2155
2156    def tuple_sql(self, expression: exp.Tuple) -> str:
2157        return f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
2158
2159    def update_sql(self, expression: exp.Update) -> str:
2160        this = self.sql(expression, "this")
2161        set_sql = self.expressions(expression, flat=True)
2162        from_sql = self.sql(expression, "from")
2163        where_sql = self.sql(expression, "where")
2164        returning = self.sql(expression, "returning")
2165        order = self.sql(expression, "order")
2166        limit = self.sql(expression, "limit")
2167        if self.RETURNING_END:
2168            expression_sql = f"{from_sql}{where_sql}{returning}"
2169        else:
2170            expression_sql = f"{returning}{from_sql}{where_sql}"
2171        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
2172        return self.prepend_ctes(expression, sql)
2173
2174    def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str:
2175        values_as_table = values_as_table and self.VALUES_AS_TABLE
2176
2177        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
2178        if values_as_table or not expression.find_ancestor(exp.From, exp.Join):
2179            args = self.expressions(expression)
2180            alias = self.sql(expression, "alias")
2181            values = f"VALUES{self.seg('')}{args}"
2182            values = (
2183                f"({values})"
2184                if self.WRAP_DERIVED_VALUES
2185                and (alias or isinstance(expression.parent, (exp.From, exp.Table)))
2186                else values
2187            )
2188            return f"{values} AS {alias}" if alias else values
2189
2190        # Converts `VALUES...` expression into a series of select unions.
2191        alias_node = expression.args.get("alias")
2192        column_names = alias_node and alias_node.columns
2193
2194        selects: t.List[exp.Query] = []
2195
2196        for i, tup in enumerate(expression.expressions):
2197            row = tup.expressions
2198
2199            if i == 0 and column_names:
2200                row = [
2201                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
2202                ]
2203
2204            selects.append(exp.Select(expressions=row))
2205
2206        if self.pretty:
2207            # This may result in poor performance for large-cardinality `VALUES` tables, due to
2208            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
2209            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
2210            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
2211            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
2212
2213        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
2214        unions = " UNION ALL ".join(self.sql(select) for select in selects)
2215        return f"({unions}){alias}"
2216
2217    def var_sql(self, expression: exp.Var) -> str:
2218        return self.sql(expression, "this")
2219
2220    @unsupported_args("expressions")
2221    def into_sql(self, expression: exp.Into) -> str:
2222        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
2223        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
2224        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
2225
2226    def from_sql(self, expression: exp.From) -> str:
2227        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
2228
2229    def groupingsets_sql(self, expression: exp.GroupingSets) -> str:
2230        grouping_sets = self.expressions(expression, indent=False)
2231        return f"GROUPING SETS {self.wrap(grouping_sets)}"
2232
2233    def rollup_sql(self, expression: exp.Rollup) -> str:
2234        expressions = self.expressions(expression, indent=False)
2235        return f"ROLLUP {self.wrap(expressions)}" if expressions else "WITH ROLLUP"
2236
2237    def cube_sql(self, expression: exp.Cube) -> str:
2238        expressions = self.expressions(expression, indent=False)
2239        return f"CUBE {self.wrap(expressions)}" if expressions else "WITH CUBE"
2240
2241    def group_sql(self, expression: exp.Group) -> str:
2242        group_by_all = expression.args.get("all")
2243        if group_by_all is True:
2244            modifier = " ALL"
2245        elif group_by_all is False:
2246            modifier = " DISTINCT"
2247        else:
2248            modifier = ""
2249
2250        group_by = self.op_expressions(f"GROUP BY{modifier}", expression)
2251
2252        grouping_sets = self.expressions(expression, key="grouping_sets")
2253        cube = self.expressions(expression, key="cube")
2254        rollup = self.expressions(expression, key="rollup")
2255
2256        groupings = csv(
2257            self.seg(grouping_sets) if grouping_sets else "",
2258            self.seg(cube) if cube else "",
2259            self.seg(rollup) if rollup else "",
2260            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
2261            sep=self.GROUPINGS_SEP,
2262        )
2263
2264        if (
2265            expression.expressions
2266            and groupings
2267            and groupings.strip() not in ("WITH CUBE", "WITH ROLLUP")
2268        ):
2269            group_by = f"{group_by}{self.GROUPINGS_SEP}"
2270
2271        return f"{group_by}{groupings}"
2272
2273    def having_sql(self, expression: exp.Having) -> str:
2274        this = self.indent(self.sql(expression, "this"))
2275        return f"{self.seg('HAVING')}{self.sep()}{this}"
2276
2277    def connect_sql(self, expression: exp.Connect) -> str:
2278        start = self.sql(expression, "start")
2279        start = self.seg(f"START WITH {start}") if start else ""
2280        nocycle = " NOCYCLE" if expression.args.get("nocycle") else ""
2281        connect = self.sql(expression, "connect")
2282        connect = self.seg(f"CONNECT BY{nocycle} {connect}")
2283        return start + connect
2284
2285    def prior_sql(self, expression: exp.Prior) -> str:
2286        return f"PRIOR {self.sql(expression, 'this')}"
2287
2288    def join_sql(self, expression: exp.Join) -> str:
2289        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
2290            side = None
2291        else:
2292            side = expression.side
2293
2294        op_sql = " ".join(
2295            op
2296            for op in (
2297                expression.method,
2298                "GLOBAL" if expression.args.get("global") else None,
2299                side,
2300                expression.kind,
2301                expression.hint if self.JOIN_HINTS else None,
2302            )
2303            if op
2304        )
2305        match_cond = self.sql(expression, "match_condition")
2306        match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
2307        on_sql = self.sql(expression, "on")
2308        using = expression.args.get("using")
2309
2310        if not on_sql and using:
2311            on_sql = csv(*(self.sql(column) for column in using))
2312
2313        this = expression.this
2314        this_sql = self.sql(this)
2315
2316        exprs = self.expressions(expression)
2317        if exprs:
2318            this_sql = f"{this_sql},{self.seg(exprs)}"
2319
2320        if on_sql:
2321            on_sql = self.indent(on_sql, skip_first=True)
2322            space = self.seg(" " * self.pad) if self.pretty else " "
2323            if using:
2324                on_sql = f"{space}USING ({on_sql})"
2325            else:
2326                on_sql = f"{space}ON {on_sql}"
2327        elif not op_sql:
2328            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
2329                return f" {this_sql}"
2330
2331            return f", {this_sql}"
2332
2333        if op_sql != "STRAIGHT_JOIN":
2334            op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
2335
2336        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2337        return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}{pivots}"
2338
2339    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->", wrap: bool = True) -> str:
2340        args = self.expressions(expression, flat=True)
2341        args = f"({args})" if wrap and len(args.split(",")) > 1 else args
2342        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
2343
2344    def lateral_op(self, expression: exp.Lateral) -> str:
2345        cross_apply = expression.args.get("cross_apply")
2346
2347        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
2348        if cross_apply is True:
2349            op = "INNER JOIN "
2350        elif cross_apply is False:
2351            op = "LEFT JOIN "
2352        else:
2353            op = ""
2354
2355        return f"{op}LATERAL"
2356
2357    def lateral_sql(self, expression: exp.Lateral) -> str:
2358        this = self.sql(expression, "this")
2359
2360        if expression.args.get("view"):
2361            alias = expression.args["alias"]
2362            columns = self.expressions(alias, key="columns", flat=True)
2363            table = f" {alias.name}" if alias.name else ""
2364            columns = f" AS {columns}" if columns else ""
2365            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
2366            return f"{op_sql}{self.sep()}{this}{table}{columns}"
2367
2368        alias = self.sql(expression, "alias")
2369        alias = f" AS {alias}" if alias else ""
2370
2371        ordinality = expression.args.get("ordinality") or ""
2372        if ordinality:
2373            ordinality = f" WITH ORDINALITY{alias}"
2374            alias = ""
2375
2376        return f"{self.lateral_op(expression)} {this}{alias}{ordinality}"
2377
2378    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
2379        this = self.sql(expression, "this")
2380
2381        args = [
2382            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
2383            for e in (expression.args.get(k) for k in ("offset", "expression"))
2384            if e
2385        ]
2386
2387        args_sql = ", ".join(self.sql(e) for e in args)
2388        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
2389        expressions = self.expressions(expression, flat=True)
2390        limit_options = self.sql(expression, "limit_options")
2391        expressions = f" BY {expressions}" if expressions else ""
2392
2393        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{limit_options}{expressions}"
2394
2395    def offset_sql(self, expression: exp.Offset) -> str:
2396        this = self.sql(expression, "this")
2397        value = expression.expression
2398        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
2399        expressions = self.expressions(expression, flat=True)
2400        expressions = f" BY {expressions}" if expressions else ""
2401        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
2402
2403    def setitem_sql(self, expression: exp.SetItem) -> str:
2404        kind = self.sql(expression, "kind")
2405        kind = f"{kind} " if kind else ""
2406        this = self.sql(expression, "this")
2407        expressions = self.expressions(expression)
2408        collate = self.sql(expression, "collate")
2409        collate = f" COLLATE {collate}" if collate else ""
2410        global_ = "GLOBAL " if expression.args.get("global") else ""
2411        return f"{global_}{kind}{this}{expressions}{collate}"
2412
2413    def set_sql(self, expression: exp.Set) -> str:
2414        expressions = f" {self.expressions(expression, flat=True)}"
2415        tag = " TAG" if expression.args.get("tag") else ""
2416        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
2417
2418    def queryband_sql(self, expression: exp.QueryBand) -> str:
2419        this = self.sql(expression, "this")
2420        update = " UPDATE" if expression.args.get("update") else ""
2421        scope = self.sql(expression, "scope")
2422        scope = f" FOR {scope}" if scope else ""
2423
2424        return f"QUERY_BAND = {this}{update}{scope}"
2425
2426    def pragma_sql(self, expression: exp.Pragma) -> str:
2427        return f"PRAGMA {self.sql(expression, 'this')}"
2428
2429    def lock_sql(self, expression: exp.Lock) -> str:
2430        if not self.LOCKING_READS_SUPPORTED:
2431            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
2432            return ""
2433
2434        update = expression.args["update"]
2435        key = expression.args.get("key")
2436        if update:
2437            lock_type = "FOR NO KEY UPDATE" if key else "FOR UPDATE"
2438        else:
2439            lock_type = "FOR KEY SHARE" if key else "FOR SHARE"
2440        expressions = self.expressions(expression, flat=True)
2441        expressions = f" OF {expressions}" if expressions else ""
2442        wait = expression.args.get("wait")
2443
2444        if wait is not None:
2445            if isinstance(wait, exp.Literal):
2446                wait = f" WAIT {self.sql(wait)}"
2447            else:
2448                wait = " NOWAIT" if wait else " SKIP LOCKED"
2449
2450        return f"{lock_type}{expressions}{wait or ''}"
2451
2452    def literal_sql(self, expression: exp.Literal) -> str:
2453        text = expression.this or ""
2454        if expression.is_string:
2455            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
2456        return text
2457
2458    def escape_str(self, text: str, escape_backslash: bool = True) -> str:
2459        if self.dialect.ESCAPED_SEQUENCES:
2460            to_escaped = self.dialect.ESCAPED_SEQUENCES
2461            text = "".join(
2462                to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text
2463            )
2464
2465        return self._replace_line_breaks(text).replace(
2466            self.dialect.QUOTE_END, self._escaped_quote_end
2467        )
2468
2469    def loaddata_sql(self, expression: exp.LoadData) -> str:
2470        local = " LOCAL" if expression.args.get("local") else ""
2471        inpath = f" INPATH {self.sql(expression, 'inpath')}"
2472        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
2473        this = f" INTO TABLE {self.sql(expression, 'this')}"
2474        partition = self.sql(expression, "partition")
2475        partition = f" {partition}" if partition else ""
2476        input_format = self.sql(expression, "input_format")
2477        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
2478        serde = self.sql(expression, "serde")
2479        serde = f" SERDE {serde}" if serde else ""
2480        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
2481
2482    def null_sql(self, *_) -> str:
2483        return "NULL"
2484
2485    def boolean_sql(self, expression: exp.Boolean) -> str:
2486        return "TRUE" if expression.this else "FALSE"
2487
2488    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
2489        this = self.sql(expression, "this")
2490        this = f"{this} " if this else this
2491        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
2492        return self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
2493
2494    def withfill_sql(self, expression: exp.WithFill) -> str:
2495        from_sql = self.sql(expression, "from")
2496        from_sql = f" FROM {from_sql}" if from_sql else ""
2497        to_sql = self.sql(expression, "to")
2498        to_sql = f" TO {to_sql}" if to_sql else ""
2499        step_sql = self.sql(expression, "step")
2500        step_sql = f" STEP {step_sql}" if step_sql else ""
2501        interpolated_values = [
2502            f"{self.sql(e, 'alias')} AS {self.sql(e, 'this')}"
2503            if isinstance(e, exp.Alias)
2504            else self.sql(e, "this")
2505            for e in expression.args.get("interpolate") or []
2506        ]
2507        interpolate = (
2508            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
2509        )
2510        return f"WITH FILL{from_sql}{to_sql}{step_sql}{interpolate}"
2511
2512    def cluster_sql(self, expression: exp.Cluster) -> str:
2513        return self.op_expressions("CLUSTER BY", expression)
2514
2515    def distribute_sql(self, expression: exp.Distribute) -> str:
2516        return self.op_expressions("DISTRIBUTE BY", expression)
2517
2518    def sort_sql(self, expression: exp.Sort) -> str:
2519        return self.op_expressions("SORT BY", expression)
2520
2521    def ordered_sql(self, expression: exp.Ordered) -> str:
2522        desc = expression.args.get("desc")
2523        asc = not desc
2524
2525        nulls_first = expression.args.get("nulls_first")
2526        nulls_last = not nulls_first
2527        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
2528        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
2529        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
2530
2531        this = self.sql(expression, "this")
2532
2533        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
2534        nulls_sort_change = ""
2535        if nulls_first and (
2536            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
2537        ):
2538            nulls_sort_change = " NULLS FIRST"
2539        elif (
2540            nulls_last
2541            and ((asc and nulls_are_small) or (desc and nulls_are_large))
2542            and not nulls_are_last
2543        ):
2544            nulls_sort_change = " NULLS LAST"
2545
2546        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
2547        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
2548            window = expression.find_ancestor(exp.Window, exp.Select)
2549            if isinstance(window, exp.Window) and window.args.get("spec"):
2550                self.unsupported(
2551                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
2552                )
2553                nulls_sort_change = ""
2554            elif self.NULL_ORDERING_SUPPORTED is False and (
2555                (asc and nulls_sort_change == " NULLS LAST")
2556                or (desc and nulls_sort_change == " NULLS FIRST")
2557            ):
2558                # BigQuery does not allow these ordering/nulls combinations when used under
2559                # an aggregation func or under a window containing one
2560                ancestor = expression.find_ancestor(exp.AggFunc, exp.Window, exp.Select)
2561
2562                if isinstance(ancestor, exp.Window):
2563                    ancestor = ancestor.this
2564                if isinstance(ancestor, exp.AggFunc):
2565                    self.unsupported(
2566                        f"'{nulls_sort_change.strip()}' translation not supported for aggregate functions with {sort_order} sort order"
2567                    )
2568                    nulls_sort_change = ""
2569            elif self.NULL_ORDERING_SUPPORTED is None:
2570                if expression.this.is_int:
2571                    self.unsupported(
2572                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
2573                    )
2574                elif not isinstance(expression.this, exp.Rand):
2575                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
2576                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
2577                nulls_sort_change = ""
2578
2579        with_fill = self.sql(expression, "with_fill")
2580        with_fill = f" {with_fill}" if with_fill else ""
2581
2582        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
2583
2584    def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str:
2585        window_frame = self.sql(expression, "window_frame")
2586        window_frame = f"{window_frame} " if window_frame else ""
2587
2588        this = self.sql(expression, "this")
2589
2590        return f"{window_frame}{this}"
2591
2592    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
2593        partition = self.partition_by_sql(expression)
2594        order = self.sql(expression, "order")
2595        measures = self.expressions(expression, key="measures")
2596        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
2597        rows = self.sql(expression, "rows")
2598        rows = self.seg(rows) if rows else ""
2599        after = self.sql(expression, "after")
2600        after = self.seg(after) if after else ""
2601        pattern = self.sql(expression, "pattern")
2602        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
2603        definition_sqls = [
2604            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
2605            for definition in expression.args.get("define", [])
2606        ]
2607        definitions = self.expressions(sqls=definition_sqls)
2608        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
2609        body = "".join(
2610            (
2611                partition,
2612                order,
2613                measures,
2614                rows,
2615                after,
2616                pattern,
2617                define,
2618            )
2619        )
2620        alias = self.sql(expression, "alias")
2621        alias = f" {alias}" if alias else ""
2622        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
2623
2624    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
2625        limit = expression.args.get("limit")
2626
2627        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
2628            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
2629        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
2630            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
2631
2632        return csv(
2633            *sqls,
2634            *[self.sql(join) for join in expression.args.get("joins") or []],
2635            self.sql(expression, "match"),
2636            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
2637            self.sql(expression, "prewhere"),
2638            self.sql(expression, "where"),
2639            self.sql(expression, "connect"),
2640            self.sql(expression, "group"),
2641            self.sql(expression, "having"),
2642            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
2643            self.sql(expression, "order"),
2644            *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
2645            *self.after_limit_modifiers(expression),
2646            self.options_modifier(expression),
2647            self.for_modifiers(expression),
2648            sep="",
2649        )
2650
2651    def options_modifier(self, expression: exp.Expression) -> str:
2652        options = self.expressions(expression, key="options")
2653        return f" {options}" if options else ""
2654
2655    def for_modifiers(self, expression: exp.Expression) -> str:
2656        for_modifiers = self.expressions(expression, key="for")
2657        return f"{self.sep()}FOR XML{self.seg(for_modifiers)}" if for_modifiers else ""
2658
2659    def queryoption_sql(self, expression: exp.QueryOption) -> str:
2660        self.unsupported("Unsupported query option.")
2661        return ""
2662
2663    def offset_limit_modifiers(
2664        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
2665    ) -> t.List[str]:
2666        return [
2667            self.sql(expression, "offset") if fetch else self.sql(limit),
2668            self.sql(limit) if fetch else self.sql(expression, "offset"),
2669        ]
2670
2671    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
2672        locks = self.expressions(expression, key="locks", sep=" ")
2673        locks = f" {locks}" if locks else ""
2674        return [locks, self.sql(expression, "sample")]
2675
2676    def select_sql(self, expression: exp.Select) -> str:
2677        into = expression.args.get("into")
2678        if not self.SUPPORTS_SELECT_INTO and into:
2679            into.pop()
2680
2681        hint = self.sql(expression, "hint")
2682        distinct = self.sql(expression, "distinct")
2683        distinct = f" {distinct}" if distinct else ""
2684        kind = self.sql(expression, "kind")
2685
2686        limit = expression.args.get("limit")
2687        if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP:
2688            top = self.limit_sql(limit, top=True)
2689            limit.pop()
2690        else:
2691            top = ""
2692
2693        expressions = self.expressions(expression)
2694
2695        if kind:
2696            if kind in self.SELECT_KINDS:
2697                kind = f" AS {kind}"
2698            else:
2699                if kind == "STRUCT":
2700                    expressions = self.expressions(
2701                        sqls=[
2702                            self.sql(
2703                                exp.Struct(
2704                                    expressions=[
2705                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
2706                                        if isinstance(e, exp.Alias)
2707                                        else e
2708                                        for e in expression.expressions
2709                                    ]
2710                                )
2711                            )
2712                        ]
2713                    )
2714                kind = ""
2715
2716        operation_modifiers = self.expressions(expression, key="operation_modifiers", sep=" ")
2717        operation_modifiers = f"{self.sep()}{operation_modifiers}" if operation_modifiers else ""
2718
2719        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
2720        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
2721        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
2722        expressions = f"{self.sep()}{expressions}" if expressions else expressions
2723        sql = self.query_modifiers(
2724            expression,
2725            f"SELECT{top_distinct}{operation_modifiers}{kind}{expressions}",
2726            self.sql(expression, "into", comment=False),
2727            self.sql(expression, "from", comment=False),
2728        )
2729
2730        # If both the CTE and SELECT clauses have comments, generate the latter earlier
2731        if expression.args.get("with"):
2732            sql = self.maybe_comment(sql, expression)
2733            expression.pop_comments()
2734
2735        sql = self.prepend_ctes(expression, sql)
2736
2737        if not self.SUPPORTS_SELECT_INTO and into:
2738            if into.args.get("temporary"):
2739                table_kind = " TEMPORARY"
2740            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
2741                table_kind = " UNLOGGED"
2742            else:
2743                table_kind = ""
2744            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
2745
2746        return sql
2747
2748    def schema_sql(self, expression: exp.Schema) -> str:
2749        this = self.sql(expression, "this")
2750        sql = self.schema_columns_sql(expression)
2751        return f"{this} {sql}" if this and sql else this or sql
2752
2753    def schema_columns_sql(self, expression: exp.Schema) -> str:
2754        if expression.expressions:
2755            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
2756        return ""
2757
2758    def star_sql(self, expression: exp.Star) -> str:
2759        except_ = self.expressions(expression, key="except", flat=True)
2760        except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
2761        replace = self.expressions(expression, key="replace", flat=True)
2762        replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
2763        rename = self.expressions(expression, key="rename", flat=True)
2764        rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
2765        return f"*{except_}{replace}{rename}"
2766
2767    def parameter_sql(self, expression: exp.Parameter) -> str:
2768        this = self.sql(expression, "this")
2769        return f"{self.PARAMETER_TOKEN}{this}"
2770
2771    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
2772        this = self.sql(expression, "this")
2773        kind = expression.text("kind")
2774        if kind:
2775            kind = f"{kind}."
2776        return f"@@{kind}{this}"
2777
2778    def placeholder_sql(self, expression: exp.Placeholder) -> str:
2779        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?"
2780
2781    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
2782        alias = self.sql(expression, "alias")
2783        alias = f"{sep}{alias}" if alias else ""
2784        sample = self.sql(expression, "sample")
2785        if self.dialect.ALIAS_POST_TABLESAMPLE and sample:
2786            alias = f"{sample}{alias}"
2787
2788            # Set to None so it's not generated again by self.query_modifiers()
2789            expression.set("sample", None)
2790
2791        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2792        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
2793        return self.prepend_ctes(expression, sql)
2794
2795    def qualify_sql(self, expression: exp.Qualify) -> str:
2796        this = self.indent(self.sql(expression, "this"))
2797        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
2798
2799    def unnest_sql(self, expression: exp.Unnest) -> str:
2800        args = self.expressions(expression, flat=True)
2801
2802        alias = expression.args.get("alias")
2803        offset = expression.args.get("offset")
2804
2805        if self.UNNEST_WITH_ORDINALITY:
2806            if alias and isinstance(offset, exp.Expression):
2807                alias.append("columns", offset)
2808
2809        if alias and self.dialect.UNNEST_COLUMN_ONLY:
2810            columns = alias.columns
2811            alias = self.sql(columns[0]) if columns else ""
2812        else:
2813            alias = self.sql(alias)
2814
2815        alias = f" AS {alias}" if alias else alias
2816        if self.UNNEST_WITH_ORDINALITY:
2817            suffix = f" WITH ORDINALITY{alias}" if offset else alias
2818        else:
2819            if isinstance(offset, exp.Expression):
2820                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
2821            elif offset:
2822                suffix = f"{alias} WITH OFFSET"
2823            else:
2824                suffix = alias
2825
2826        return f"UNNEST({args}){suffix}"
2827
2828    def prewhere_sql(self, expression: exp.PreWhere) -> str:
2829        return ""
2830
2831    def where_sql(self, expression: exp.Where) -> str:
2832        this = self.indent(self.sql(expression, "this"))
2833        return f"{self.seg('WHERE')}{self.sep()}{this}"
2834
2835    def window_sql(self, expression: exp.Window) -> str:
2836        this = self.sql(expression, "this")
2837        partition = self.partition_by_sql(expression)
2838        order = expression.args.get("order")
2839        order = self.order_sql(order, flat=True) if order else ""
2840        spec = self.sql(expression, "spec")
2841        alias = self.sql(expression, "alias")
2842        over = self.sql(expression, "over") or "OVER"
2843
2844        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
2845
2846        first = expression.args.get("first")
2847        if first is None:
2848            first = ""
2849        else:
2850            first = "FIRST" if first else "LAST"
2851
2852        if not partition and not order and not spec and alias:
2853            return f"{this} {alias}"
2854
2855        args = self.format_args(
2856            *[arg for arg in (alias, first, partition, order, spec) if arg], sep=" "
2857        )
2858        return f"{this} ({args})"
2859
2860    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
2861        partition = self.expressions(expression, key="partition_by", flat=True)
2862        return f"PARTITION BY {partition}" if partition else ""
2863
2864    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
2865        kind = self.sql(expression, "kind")
2866        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
2867        end = (
2868            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
2869            or "CURRENT ROW"
2870        )
2871
2872        window_spec = f"{kind} BETWEEN {start} AND {end}"
2873
2874        exclude = self.sql(expression, "exclude")
2875        if exclude:
2876            if self.SUPPORTS_WINDOW_EXCLUDE:
2877                window_spec += f" EXCLUDE {exclude}"
2878            else:
2879                self.unsupported("EXCLUDE clause is not supported in the WINDOW clause")
2880
2881        return window_spec
2882
2883    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
2884        this = self.sql(expression, "this")
2885        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
2886        return f"{this} WITHIN GROUP ({expression_sql})"
2887
2888    def between_sql(self, expression: exp.Between) -> str:
2889        this = self.sql(expression, "this")
2890        low = self.sql(expression, "low")
2891        high = self.sql(expression, "high")
2892        symmetric = expression.args.get("symmetric")
2893
2894        if symmetric and not self.SUPPORTS_BETWEEN_FLAGS:
2895            return f"({this} BETWEEN {low} AND {high} OR {this} BETWEEN {high} AND {low})"
2896
2897        flag = (
2898            " SYMMETRIC"
2899            if symmetric
2900            else " ASYMMETRIC"
2901            if symmetric is False and self.SUPPORTS_BETWEEN_FLAGS
2902            else ""  # silently drop ASYMMETRIC – semantics identical
2903        )
2904        return f"{this} BETWEEN{flag} {low} AND {high}"
2905
2906    def bracket_offset_expressions(
2907        self, expression: exp.Bracket, index_offset: t.Optional[int] = None
2908    ) -> t.List[exp.Expression]:
2909        return apply_index_offset(
2910            expression.this,
2911            expression.expressions,
2912            (index_offset or self.dialect.INDEX_OFFSET) - expression.args.get("offset", 0),
2913            dialect=self.dialect,
2914        )
2915
2916    def bracket_sql(self, expression: exp.Bracket) -> str:
2917        expressions = self.bracket_offset_expressions(expression)
2918        expressions_sql = ", ".join(self.sql(e) for e in expressions)
2919        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
2920
2921    def all_sql(self, expression: exp.All) -> str:
2922        this = self.sql(expression, "this")
2923        if not isinstance(expression.this, (exp.Tuple, exp.Paren)):
2924            this = self.wrap(this)
2925        return f"ALL {this}"
2926
2927    def any_sql(self, expression: exp.Any) -> str:
2928        this = self.sql(expression, "this")
2929        if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)):
2930            if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
2931                this = self.wrap(this)
2932            return f"ANY{this}"
2933        return f"ANY {this}"
2934
2935    def exists_sql(self, expression: exp.Exists) -> str:
2936        return f"EXISTS{self.wrap(expression)}"
2937
2938    def case_sql(self, expression: exp.Case) -> str:
2939        this = self.sql(expression, "this")
2940        statements = [f"CASE {this}" if this else "CASE"]
2941
2942        for e in expression.args["ifs"]:
2943            statements.append(f"WHEN {self.sql(e, 'this')}")
2944            statements.append(f"THEN {self.sql(e, 'true')}")
2945
2946        default = self.sql(expression, "default")
2947
2948        if default:
2949            statements.append(f"ELSE {default}")
2950
2951        statements.append("END")
2952
2953        if self.pretty and self.too_wide(statements):
2954            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
2955
2956        return " ".join(statements)
2957
2958    def constraint_sql(self, expression: exp.Constraint) -> str:
2959        this = self.sql(expression, "this")
2960        expressions = self.expressions(expression, flat=True)
2961        return f"CONSTRAINT {this} {expressions}"
2962
2963    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
2964        order = expression.args.get("order")
2965        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
2966        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
2967
2968    def extract_sql(self, expression: exp.Extract) -> str:
2969        from sqlglot.dialects.dialect import map_date_part
2970
2971        this = (
2972            map_date_part(expression.this, self.dialect)
2973            if self.NORMALIZE_EXTRACT_DATE_PARTS
2974            else expression.this
2975        )
2976        this_sql = self.sql(this) if self.EXTRACT_ALLOWS_QUOTES else this.name
2977        expression_sql = self.sql(expression, "expression")
2978
2979        return f"EXTRACT({this_sql} FROM {expression_sql})"
2980
2981    def trim_sql(self, expression: exp.Trim) -> str:
2982        trim_type = self.sql(expression, "position")
2983
2984        if trim_type == "LEADING":
2985            func_name = "LTRIM"
2986        elif trim_type == "TRAILING":
2987            func_name = "RTRIM"
2988        else:
2989            func_name = "TRIM"
2990
2991        return self.func(func_name, expression.this, expression.expression)
2992
2993    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
2994        args = expression.expressions
2995        if isinstance(expression, exp.ConcatWs):
2996            args = args[1:]  # Skip the delimiter
2997
2998        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2999            args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args]
3000
3001        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
3002
3003            def _wrap_with_coalesce(e: exp.Expression) -> exp.Expression:
3004                if not e.type:
3005                    from sqlglot.optimizer.annotate_types import annotate_types
3006
3007                    e = annotate_types(e, dialect=self.dialect)
3008
3009                if e.is_string or e.is_type(exp.DataType.Type.ARRAY):
3010                    return e
3011
3012                return exp.func("coalesce", e, exp.Literal.string(""))
3013
3014            args = [_wrap_with_coalesce(e) for e in args]
3015
3016        return args
3017
3018    def concat_sql(self, expression: exp.Concat) -> str:
3019        expressions = self.convert_concat_args(expression)
3020
3021        # Some dialects don't allow a single-argument CONCAT call
3022        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
3023            return self.sql(expressions[0])
3024
3025        return self.func("CONCAT", *expressions)
3026
3027    def concatws_sql(self, expression: exp.ConcatWs) -> str:
3028        return self.func(
3029            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
3030        )
3031
3032    def check_sql(self, expression: exp.Check) -> str:
3033        this = self.sql(expression, key="this")
3034        return f"CHECK ({this})"
3035
3036    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
3037        expressions = self.expressions(expression, flat=True)
3038        expressions = f" ({expressions})" if expressions else ""
3039        reference = self.sql(expression, "reference")
3040        reference = f" {reference}" if reference else ""
3041        delete = self.sql(expression, "delete")
3042        delete = f" ON DELETE {delete}" if delete else ""
3043        update = self.sql(expression, "update")
3044        update = f" ON UPDATE {update}" if update else ""
3045        options = self.expressions(expression, key="options", flat=True, sep=" ")
3046        options = f" {options}" if options else ""
3047        return f"FOREIGN KEY{expressions}{reference}{delete}{update}{options}"
3048
3049    def primarykey_sql(self, expression: exp.PrimaryKey) -> str:
3050        expressions = self.expressions(expression, flat=True)
3051        include = self.sql(expression, "include")
3052        options = self.expressions(expression, key="options", flat=True, sep=" ")
3053        options = f" {options}" if options else ""
3054        return f"PRIMARY KEY ({expressions}){include}{options}"
3055
3056    def if_sql(self, expression: exp.If) -> str:
3057        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
3058
3059    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
3060        modifier = expression.args.get("modifier")
3061        modifier = f" {modifier}" if modifier else ""
3062        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
3063
3064    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
3065        return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"
3066
3067    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
3068        path = self.expressions(expression, sep="", flat=True).lstrip(".")
3069
3070        if expression.args.get("escape"):
3071            path = self.escape_str(path)
3072
3073        if self.QUOTE_JSON_PATH:
3074            path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
3075
3076        return path
3077
3078    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
3079        if isinstance(expression, exp.JSONPathPart):
3080            transform = self.TRANSFORMS.get(expression.__class__)
3081            if not callable(transform):
3082                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
3083                return ""
3084
3085            return transform(self, expression)
3086
3087        if isinstance(expression, int):
3088            return str(expression)
3089
3090        if self._quote_json_path_key_using_brackets and self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
3091            escaped = expression.replace("'", "\\'")
3092            escaped = f"\\'{expression}\\'"
3093        else:
3094            escaped = expression.replace('"', '\\"')
3095            escaped = f'"{escaped}"'
3096
3097        return escaped
3098
3099    def formatjson_sql(self, expression: exp.FormatJson) -> str:
3100        return f"{self.sql(expression, 'this')} FORMAT JSON"
3101
3102    def formatphrase_sql(self, expression: exp.FormatPhrase) -> str:
3103        # Output the Teradata column FORMAT override.
3104        # https://docs.teradata.com/r/Enterprise_IntelliFlex_VMware/SQL-Data-Types-and-Literals/Data-Type-Formats-and-Format-Phrases/FORMAT
3105        this = self.sql(expression, "this")
3106        fmt = self.sql(expression, "format")
3107        return f"{this} (FORMAT {fmt})"
3108
3109    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
3110        null_handling = expression.args.get("null_handling")
3111        null_handling = f" {null_handling}" if null_handling else ""
3112
3113        unique_keys = expression.args.get("unique_keys")
3114        if unique_keys is not None:
3115            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
3116        else:
3117            unique_keys = ""
3118
3119        return_type = self.sql(expression, "return_type")
3120        return_type = f" RETURNING {return_type}" if return_type else ""
3121        encoding = self.sql(expression, "encoding")
3122        encoding = f" ENCODING {encoding}" if encoding else ""
3123
3124        return self.func(
3125            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
3126            *expression.expressions,
3127            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
3128        )
3129
3130    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
3131        return self.jsonobject_sql(expression)
3132
3133    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
3134        null_handling = expression.args.get("null_handling")
3135        null_handling = f" {null_handling}" if null_handling else ""
3136        return_type = self.sql(expression, "return_type")
3137        return_type = f" RETURNING {return_type}" if return_type else ""
3138        strict = " STRICT" if expression.args.get("strict") else ""
3139        return self.func(
3140            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
3141        )
3142
3143    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
3144        this = self.sql(expression, "this")
3145        order = self.sql(expression, "order")
3146        null_handling = expression.args.get("null_handling")
3147        null_handling = f" {null_handling}" if null_handling else ""
3148        return_type = self.sql(expression, "return_type")
3149        return_type = f" RETURNING {return_type}" if return_type else ""
3150        strict = " STRICT" if expression.args.get("strict") else ""
3151        return self.func(
3152            "JSON_ARRAYAGG",
3153            this,
3154            suffix=f"{order}{null_handling}{return_type}{strict})",
3155        )
3156
3157    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
3158        path = self.sql(expression, "path")
3159        path = f" PATH {path}" if path else ""
3160        nested_schema = self.sql(expression, "nested_schema")
3161
3162        if nested_schema:
3163            return f"NESTED{path} {nested_schema}"
3164
3165        this = self.sql(expression, "this")
3166        kind = self.sql(expression, "kind")
3167        kind = f" {kind}" if kind else ""
3168        return f"{this}{kind}{path}"
3169
3170    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
3171        return self.func("COLUMNS", *expression.expressions)
3172
3173    def jsontable_sql(self, expression: exp.JSONTable) -> str:
3174        this = self.sql(expression, "this")
3175        path = self.sql(expression, "path")
3176        path = f", {path}" if path else ""
3177        error_handling = expression.args.get("error_handling")
3178        error_handling = f" {error_handling}" if error_handling else ""
3179        empty_handling = expression.args.get("empty_handling")
3180        empty_handling = f" {empty_handling}" if empty_handling else ""
3181        schema = self.sql(expression, "schema")
3182        return self.func(
3183            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
3184        )
3185
3186    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
3187        this = self.sql(expression, "this")
3188        kind = self.sql(expression, "kind")
3189        path = self.sql(expression, "path")
3190        path = f" {path}" if path else ""
3191        as_json = " AS JSON" if expression.args.get("as_json") else ""
3192        return f"{this} {kind}{path}{as_json}"
3193
3194    def openjson_sql(self, expression: exp.OpenJSON) -> str:
3195        this = self.sql(expression, "this")
3196        path = self.sql(expression, "path")
3197        path = f", {path}" if path else ""
3198        expressions = self.expressions(expression)
3199        with_ = (
3200            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
3201            if expressions
3202            else ""
3203        )
3204        return f"OPENJSON({this}{path}){with_}"
3205
3206    def in_sql(self, expression: exp.In) -> str:
3207        query = expression.args.get("query")
3208        unnest = expression.args.get("unnest")
3209        field = expression.args.get("field")
3210        is_global = " GLOBAL" if expression.args.get("is_global") else ""
3211
3212        if query:
3213            in_sql = self.sql(query)
3214        elif unnest:
3215            in_sql = self.in_unnest_op(unnest)
3216        elif field:
3217            in_sql = self.sql(field)
3218        else:
3219            in_sql = f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
3220
3221        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
3222
3223    def in_unnest_op(self, unnest: exp.Unnest) -> str:
3224        return f"(SELECT {self.sql(unnest)})"
3225
3226    def interval_sql(self, expression: exp.Interval) -> str:
3227        unit = self.sql(expression, "unit")
3228        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
3229            unit = self.TIME_PART_SINGULARS.get(unit, unit)
3230        unit = f" {unit}" if unit else ""
3231
3232        if self.SINGLE_STRING_INTERVAL:
3233            this = expression.this.name if expression.this else ""
3234            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
3235
3236        this = self.sql(expression, "this")
3237        if this:
3238            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
3239            this = f" {this}" if unwrapped else f" ({this})"
3240
3241        return f"INTERVAL{this}{unit}"
3242
3243    def return_sql(self, expression: exp.Return) -> str:
3244        return f"RETURN {self.sql(expression, 'this')}"
3245
3246    def reference_sql(self, expression: exp.Reference) -> str:
3247        this = self.sql(expression, "this")
3248        expressions = self.expressions(expression, flat=True)
3249        expressions = f"({expressions})" if expressions else ""
3250        options = self.expressions(expression, key="options", flat=True, sep=" ")
3251        options = f" {options}" if options else ""
3252        return f"REFERENCES {this}{expressions}{options}"
3253
3254    def anonymous_sql(self, expression: exp.Anonymous) -> str:
3255        # We don't normalize qualified functions such as a.b.foo(), because they can be case-sensitive
3256        parent = expression.parent
3257        is_qualified = isinstance(parent, exp.Dot) and expression is parent.expression
3258        return self.func(
3259            self.sql(expression, "this"), *expression.expressions, normalize=not is_qualified
3260        )
3261
3262    def paren_sql(self, expression: exp.Paren) -> str:
3263        sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
3264        return f"({sql}{self.seg(')', sep='')}"
3265
3266    def neg_sql(self, expression: exp.Neg) -> str:
3267        # This makes sure we don't convert "- - 5" to "--5", which is a comment
3268        this_sql = self.sql(expression, "this")
3269        sep = " " if this_sql[0] == "-" else ""
3270        return f"-{sep}{this_sql}"
3271
3272    def not_sql(self, expression: exp.Not) -> str:
3273        return f"NOT {self.sql(expression, 'this')}"
3274
3275    def alias_sql(self, expression: exp.Alias) -> str:
3276        alias = self.sql(expression, "alias")
3277        alias = f" AS {alias}" if alias else ""
3278        return f"{self.sql(expression, 'this')}{alias}"
3279
3280    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
3281        alias = expression.args["alias"]
3282
3283        parent = expression.parent
3284        pivot = parent and parent.parent
3285
3286        if isinstance(pivot, exp.Pivot) and pivot.unpivot:
3287            identifier_alias = isinstance(alias, exp.Identifier)
3288            literal_alias = isinstance(alias, exp.Literal)
3289
3290            if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
3291                alias.replace(exp.Literal.string(alias.output_name))
3292            elif not identifier_alias and literal_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
3293                alias.replace(exp.to_identifier(alias.output_name))
3294
3295        return self.alias_sql(expression)
3296
3297    def aliases_sql(self, expression: exp.Aliases) -> str:
3298        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
3299
3300    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
3301        this = self.sql(expression, "this")
3302        index = self.sql(expression, "expression")
3303        return f"{this} AT {index}"
3304
3305    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
3306        this = self.sql(expression, "this")
3307        zone = self.sql(expression, "zone")
3308        return f"{this} AT TIME ZONE {zone}"
3309
3310    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
3311        this = self.sql(expression, "this")
3312        zone = self.sql(expression, "zone")
3313        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
3314
3315    def add_sql(self, expression: exp.Add) -> str:
3316        return self.binary(expression, "+")
3317
3318    def and_sql(
3319        self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None
3320    ) -> str:
3321        return self.connector_sql(expression, "AND", stack)
3322
3323    def or_sql(
3324        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
3325    ) -> str:
3326        return self.connector_sql(expression, "OR", stack)
3327
3328    def xor_sql(
3329        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
3330    ) -> str:
3331        return self.connector_sql(expression, "XOR", stack)
3332
3333    def connector_sql(
3334        self,
3335        expression: exp.Connector,
3336        op: str,
3337        stack: t.Optional[t.List[str | exp.Expression]] = None,
3338    ) -> str:
3339        if stack is not None:
3340            if expression.expressions:
3341                stack.append(self.expressions(expression, sep=f" {op} "))
3342            else:
3343                stack.append(expression.right)
3344                if expression.comments and self.comments:
3345                    for comment in expression.comments:
3346                        if comment:
3347                            op += f" /*{self.sanitize_comment(comment)}*/"
3348                stack.extend((op, expression.left))
3349            return op
3350
3351        stack = [expression]
3352        sqls: t.List[str] = []
3353        ops = set()
3354
3355        while stack:
3356            node = stack.pop()
3357            if isinstance(node, exp.Connector):
3358                ops.add(getattr(self, f"{node.key}_sql")(node, stack))
3359            else:
3360                sql = self.sql(node)
3361                if sqls and sqls[-1] in ops:
3362                    sqls[-1] += f" {sql}"
3363                else:
3364                    sqls.append(sql)
3365
3366        sep = "\n" if self.pretty and self.too_wide(sqls) else " "
3367        return sep.join(sqls)
3368
3369    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
3370        return self.binary(expression, "&")
3371
3372    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
3373        return self.binary(expression, "<<")
3374
3375    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
3376        return f"~{self.sql(expression, 'this')}"
3377
3378    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
3379        return self.binary(expression, "|")
3380
3381    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
3382        return self.binary(expression, ">>")
3383
3384    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
3385        return self.binary(expression, "^")
3386
3387    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
3388        format_sql = self.sql(expression, "format")
3389        format_sql = f" FORMAT {format_sql}" if format_sql else ""
3390        to_sql = self.sql(expression, "to")
3391        to_sql = f" {to_sql}" if to_sql else ""
3392        action = self.sql(expression, "action")
3393        action = f" {action}" if action else ""
3394        default = self.sql(expression, "default")
3395        default = f" DEFAULT {default} ON CONVERSION ERROR" if default else ""
3396        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{default}{format_sql}{action})"
3397
3398    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
3399        zone = self.sql(expression, "this")
3400        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
3401
3402    def collate_sql(self, expression: exp.Collate) -> str:
3403        if self.COLLATE_IS_FUNC:
3404            return self.function_fallback_sql(expression)
3405        return self.binary(expression, "COLLATE")
3406
3407    def command_sql(self, expression: exp.Command) -> str:
3408        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
3409
3410    def comment_sql(self, expression: exp.Comment) -> str:
3411        this = self.sql(expression, "this")
3412        kind = expression.args["kind"]
3413        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
3414        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
3415        expression_sql = self.sql(expression, "expression")
3416        return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"
3417
3418    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
3419        this = self.sql(expression, "this")
3420        delete = " DELETE" if expression.args.get("delete") else ""
3421        recompress = self.sql(expression, "recompress")
3422        recompress = f" RECOMPRESS {recompress}" if recompress else ""
3423        to_disk = self.sql(expression, "to_disk")
3424        to_disk = f" TO DISK {to_disk}" if to_disk else ""
3425        to_volume = self.sql(expression, "to_volume")
3426        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
3427        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
3428
3429    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
3430        where = self.sql(expression, "where")
3431        group = self.sql(expression, "group")
3432        aggregates = self.expressions(expression, key="aggregates")
3433        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
3434
3435        if not (where or group or aggregates) and len(expression.expressions) == 1:
3436            return f"TTL {self.expressions(expression, flat=True)}"
3437
3438        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
3439
3440    def transaction_sql(self, expression: exp.Transaction) -> str:
3441        return "BEGIN"
3442
3443    def commit_sql(self, expression: exp.Commit) -> str:
3444        chain = expression.args.get("chain")
3445        if chain is not None:
3446            chain = " AND CHAIN" if chain else " AND NO CHAIN"
3447
3448        return f"COMMIT{chain or ''}"
3449
3450    def rollback_sql(self, expression: exp.Rollback) -> str:
3451        savepoint = expression.args.get("savepoint")
3452        savepoint = f" TO {savepoint}" if savepoint else ""
3453        return f"ROLLBACK{savepoint}"
3454
3455    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
3456        this = self.sql(expression, "this")
3457
3458        dtype = self.sql(expression, "dtype")
3459        if dtype:
3460            collate = self.sql(expression, "collate")
3461            collate = f" COLLATE {collate}" if collate else ""
3462            using = self.sql(expression, "using")
3463            using = f" USING {using}" if using else ""
3464            alter_set_type = self.ALTER_SET_TYPE + " " if self.ALTER_SET_TYPE else ""
3465            return f"ALTER COLUMN {this} {alter_set_type}{dtype}{collate}{using}"
3466
3467        default = self.sql(expression, "default")
3468        if default:
3469            return f"ALTER COLUMN {this} SET DEFAULT {default}"
3470
3471        comment = self.sql(expression, "comment")
3472        if comment:
3473            return f"ALTER COLUMN {this} COMMENT {comment}"
3474
3475        visible = expression.args.get("visible")
3476        if visible:
3477            return f"ALTER COLUMN {this} SET {visible}"
3478
3479        allow_null = expression.args.get("allow_null")
3480        drop = expression.args.get("drop")
3481
3482        if not drop and not allow_null:
3483            self.unsupported("Unsupported ALTER COLUMN syntax")
3484
3485        if allow_null is not None:
3486            keyword = "DROP" if drop else "SET"
3487            return f"ALTER COLUMN {this} {keyword} NOT NULL"
3488
3489        return f"ALTER COLUMN {this} DROP DEFAULT"
3490
3491    def alterindex_sql(self, expression: exp.AlterIndex) -> str:
3492        this = self.sql(expression, "this")
3493
3494        visible = expression.args.get("visible")
3495        visible_sql = "VISIBLE" if visible else "INVISIBLE"
3496
3497        return f"ALTER INDEX {this} {visible_sql}"
3498
3499    def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
3500        this = self.sql(expression, "this")
3501        if not isinstance(expression.this, exp.Var):
3502            this = f"KEY DISTKEY {this}"
3503        return f"ALTER DISTSTYLE {this}"
3504
3505    def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
3506        compound = " COMPOUND" if expression.args.get("compound") else ""
3507        this = self.sql(expression, "this")
3508        expressions = self.expressions(expression, flat=True)
3509        expressions = f"({expressions})" if expressions else ""
3510        return f"ALTER{compound} SORTKEY {this or expressions}"
3511
3512    def alterrename_sql(self, expression: exp.AlterRename, include_to: bool = True) -> str:
3513        if not self.RENAME_TABLE_WITH_DB:
3514            # Remove db from tables
3515            expression = expression.transform(
3516                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
3517            ).assert_is(exp.AlterRename)
3518        this = self.sql(expression, "this")
3519        to_kw = " TO" if include_to else ""
3520        return f"RENAME{to_kw} {this}"
3521
3522    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
3523        exists = " IF EXISTS" if expression.args.get("exists") else ""
3524        old_column = self.sql(expression, "this")
3525        new_column = self.sql(expression, "to")
3526        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
3527
3528    def alterset_sql(self, expression: exp.AlterSet) -> str:
3529        exprs = self.expressions(expression, flat=True)
3530        if self.ALTER_SET_WRAPPED:
3531            exprs = f"({exprs})"
3532
3533        return f"SET {exprs}"
3534
3535    def alter_sql(self, expression: exp.Alter) -> str:
3536        actions = expression.args["actions"]
3537
3538        if not self.dialect.ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN and isinstance(
3539            actions[0], exp.ColumnDef
3540        ):
3541            actions_sql = self.expressions(expression, key="actions", flat=True)
3542            actions_sql = f"ADD {actions_sql}"
3543        else:
3544            actions_list = []
3545            for action in actions:
3546                if isinstance(action, (exp.ColumnDef, exp.Schema)):
3547                    action_sql = self.add_column_sql(action)
3548                else:
3549                    action_sql = self.sql(action)
3550                    if isinstance(action, exp.Query):
3551                        action_sql = f"AS {action_sql}"
3552
3553                actions_list.append(action_sql)
3554
3555            actions_sql = self.format_args(*actions_list).lstrip("\n")
3556
3557        exists = " IF EXISTS" if expression.args.get("exists") else ""
3558        on_cluster = self.sql(expression, "cluster")
3559        on_cluster = f" {on_cluster}" if on_cluster else ""
3560        only = " ONLY" if expression.args.get("only") else ""
3561        options = self.expressions(expression, key="options")
3562        options = f", {options}" if options else ""
3563        kind = self.sql(expression, "kind")
3564        not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
3565        check = " WITH CHECK" if expression.args.get("check") else ""
3566
3567        return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster}{check}{self.sep()}{actions_sql}{not_valid}{options}"
3568
3569    def add_column_sql(self, expression: exp.Expression) -> str:
3570        sql = self.sql(expression)
3571        if isinstance(expression, exp.Schema):
3572            column_text = " COLUMNS"
3573        elif isinstance(expression, exp.ColumnDef) and self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
3574            column_text = " COLUMN"
3575        else:
3576            column_text = ""
3577
3578        return f"ADD{column_text} {sql}"
3579
3580    def droppartition_sql(self, expression: exp.DropPartition) -> str:
3581        expressions = self.expressions(expression)
3582        exists = " IF EXISTS " if expression.args.get("exists") else " "
3583        return f"DROP{exists}{expressions}"
3584
3585    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
3586        return f"ADD {self.expressions(expression, indent=False)}"
3587
3588    def addpartition_sql(self, expression: exp.AddPartition) -> str:
3589        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
3590        location = self.sql(expression, "location")
3591        location = f" {location}" if location else ""
3592        return f"ADD {exists}{self.sql(expression.this)}{location}"
3593
3594    def distinct_sql(self, expression: exp.Distinct) -> str:
3595        this = self.expressions(expression, flat=True)
3596
3597        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
3598            case = exp.case()
3599            for arg in expression.expressions:
3600                case = case.when(arg.is_(exp.null()), exp.null())
3601            this = self.sql(case.else_(f"({this})"))
3602
3603        this = f" {this}" if this else ""
3604
3605        on = self.sql(expression, "on")
3606        on = f" ON {on}" if on else ""
3607        return f"DISTINCT{this}{on}"
3608
3609    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
3610        return self._embed_ignore_nulls(expression, "IGNORE NULLS")
3611
3612    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
3613        return self._embed_ignore_nulls(expression, "RESPECT NULLS")
3614
3615    def havingmax_sql(self, expression: exp.HavingMax) -> str:
3616        this_sql = self.sql(expression, "this")
3617        expression_sql = self.sql(expression, "expression")
3618        kind = "MAX" if expression.args.get("max") else "MIN"
3619        return f"{this_sql} HAVING {kind} {expression_sql}"
3620
3621    def intdiv_sql(self, expression: exp.IntDiv) -> str:
3622        return self.sql(
3623            exp.Cast(
3624                this=exp.Div(this=expression.this, expression=expression.expression),
3625                to=exp.DataType(this=exp.DataType.Type.INT),
3626            )
3627        )
3628
3629    def dpipe_sql(self, expression: exp.DPipe) -> str:
3630        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
3631            return self.func(
3632                "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten())
3633            )
3634        return self.binary(expression, "||")
3635
3636    def div_sql(self, expression: exp.Div) -> str:
3637        l, r = expression.left, expression.right
3638
3639        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
3640            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
3641
3642        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
3643            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
3644                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
3645
3646        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
3647            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
3648                return self.sql(
3649                    exp.cast(
3650                        l / r,
3651                        to=exp.DataType.Type.BIGINT,
3652                    )
3653                )
3654
3655        return self.binary(expression, "/")
3656
3657    def safedivide_sql(self, expression: exp.SafeDivide) -> str:
3658        n = exp._wrap(expression.this, exp.Binary)
3659        d = exp._wrap(expression.expression, exp.Binary)
3660        return self.sql(exp.If(this=d.neq(0), true=n / d, false=exp.Null()))
3661
3662    def overlaps_sql(self, expression: exp.Overlaps) -> str:
3663        return self.binary(expression, "OVERLAPS")
3664
3665    def distance_sql(self, expression: exp.Distance) -> str:
3666        return self.binary(expression, "<->")
3667
3668    def dot_sql(self, expression: exp.Dot) -> str:
3669        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
3670
3671    def eq_sql(self, expression: exp.EQ) -> str:
3672        return self.binary(expression, "=")
3673
3674    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
3675        return self.binary(expression, ":=")
3676
3677    def escape_sql(self, expression: exp.Escape) -> str:
3678        return self.binary(expression, "ESCAPE")
3679
3680    def glob_sql(self, expression: exp.Glob) -> str:
3681        return self.binary(expression, "GLOB")
3682
3683    def gt_sql(self, expression: exp.GT) -> str:
3684        return self.binary(expression, ">")
3685
3686    def gte_sql(self, expression: exp.GTE) -> str:
3687        return self.binary(expression, ">=")
3688
3689    def is_sql(self, expression: exp.Is) -> str:
3690        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
3691            return self.sql(
3692                expression.this if expression.expression.this else exp.not_(expression.this)
3693            )
3694        return self.binary(expression, "IS")
3695
3696    def _like_sql(self, expression: exp.Like | exp.ILike) -> str:
3697        this = expression.this
3698        rhs = expression.expression
3699
3700        if isinstance(expression, exp.Like):
3701            exp_class: t.Type[exp.Like | exp.ILike] = exp.Like
3702            op = "LIKE"
3703        else:
3704            exp_class = exp.ILike
3705            op = "ILIKE"
3706
3707        if isinstance(rhs, (exp.All, exp.Any)) and not self.SUPPORTS_LIKE_QUANTIFIERS:
3708            exprs = rhs.this.unnest()
3709
3710            if isinstance(exprs, exp.Tuple):
3711                exprs = exprs.expressions
3712
3713            connective = exp.or_ if isinstance(rhs, exp.Any) else exp.and_
3714
3715            like_expr: exp.Expression = exp_class(this=this, expression=exprs[0])
3716            for expr in exprs[1:]:
3717                like_expr = connective(like_expr, exp_class(this=this, expression=expr))
3718
3719            return self.sql(like_expr)
3720
3721        return self.binary(expression, op)
3722
3723    def like_sql(self, expression: exp.Like) -> str:
3724        return self._like_sql(expression)
3725
3726    def ilike_sql(self, expression: exp.ILike) -> str:
3727        return self._like_sql(expression)
3728
3729    def similarto_sql(self, expression: exp.SimilarTo) -> str:
3730        return self.binary(expression, "SIMILAR TO")
3731
3732    def lt_sql(self, expression: exp.LT) -> str:
3733        return self.binary(expression, "<")
3734
3735    def lte_sql(self, expression: exp.LTE) -> str:
3736        return self.binary(expression, "<=")
3737
3738    def mod_sql(self, expression: exp.Mod) -> str:
3739        return self.binary(expression, "%")
3740
3741    def mul_sql(self, expression: exp.Mul) -> str:
3742        return self.binary(expression, "*")
3743
3744    def neq_sql(self, expression: exp.NEQ) -> str:
3745        return self.binary(expression, "<>")
3746
3747    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
3748        return self.binary(expression, "IS NOT DISTINCT FROM")
3749
3750    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
3751        return self.binary(expression, "IS DISTINCT FROM")
3752
3753    def slice_sql(self, expression: exp.Slice) -> str:
3754        return self.binary(expression, ":")
3755
3756    def sub_sql(self, expression: exp.Sub) -> str:
3757        return self.binary(expression, "-")
3758
3759    def trycast_sql(self, expression: exp.TryCast) -> str:
3760        return self.cast_sql(expression, safe_prefix="TRY_")
3761
3762    def jsoncast_sql(self, expression: exp.JSONCast) -> str:
3763        return self.cast_sql(expression)
3764
3765    def try_sql(self, expression: exp.Try) -> str:
3766        if not self.TRY_SUPPORTED:
3767            self.unsupported("Unsupported TRY function")
3768            return self.sql(expression, "this")
3769
3770        return self.func("TRY", expression.this)
3771
3772    def log_sql(self, expression: exp.Log) -> str:
3773        this = expression.this
3774        expr = expression.expression
3775
3776        if self.dialect.LOG_BASE_FIRST is False:
3777            this, expr = expr, this
3778        elif self.dialect.LOG_BASE_FIRST is None and expr:
3779            if this.name in ("2", "10"):
3780                return self.func(f"LOG{this.name}", expr)
3781
3782            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
3783
3784        return self.func("LOG", this, expr)
3785
3786    def use_sql(self, expression: exp.Use) -> str:
3787        kind = self.sql(expression, "kind")
3788        kind = f" {kind}" if kind else ""
3789        this = self.sql(expression, "this") or self.expressions(expression, flat=True)
3790        this = f" {this}" if this else ""
3791        return f"USE{kind}{this}"
3792
3793    def binary(self, expression: exp.Binary, op: str) -> str:
3794        sqls: t.List[str] = []
3795        stack: t.List[t.Union[str, exp.Expression]] = [expression]
3796        binary_type = type(expression)
3797
3798        while stack:
3799            node = stack.pop()
3800
3801            if type(node) is binary_type:
3802                op_func = node.args.get("operator")
3803                if op_func:
3804                    op = f"OPERATOR({self.sql(op_func)})"
3805
3806                stack.append(node.right)
3807                stack.append(f" {self.maybe_comment(op, comments=node.comments)} ")
3808                stack.append(node.left)
3809            else:
3810                sqls.append(self.sql(node))
3811
3812        return "".join(sqls)
3813
3814    def ceil_floor(self, expression: exp.Ceil | exp.Floor) -> str:
3815        to_clause = self.sql(expression, "to")
3816        if to_clause:
3817            return f"{expression.sql_name()}({self.sql(expression, 'this')} TO {to_clause})"
3818
3819        return self.function_fallback_sql(expression)
3820
3821    def function_fallback_sql(self, expression: exp.Func) -> str:
3822        args = []
3823
3824        for key in expression.arg_types:
3825            arg_value = expression.args.get(key)
3826
3827            if isinstance(arg_value, list):
3828                for value in arg_value:
3829                    args.append(value)
3830            elif arg_value is not None:
3831                args.append(arg_value)
3832
3833        if self.dialect.PRESERVE_ORIGINAL_NAMES:
3834            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
3835        else:
3836            name = expression.sql_name()
3837
3838        return self.func(name, *args)
3839
3840    def func(
3841        self,
3842        name: str,
3843        *args: t.Optional[exp.Expression | str],
3844        prefix: str = "(",
3845        suffix: str = ")",
3846        normalize: bool = True,
3847    ) -> str:
3848        name = self.normalize_func(name) if normalize else name
3849        return f"{name}{prefix}{self.format_args(*args)}{suffix}"
3850
3851    def format_args(self, *args: t.Optional[str | exp.Expression], sep: str = ", ") -> str:
3852        arg_sqls = tuple(
3853            self.sql(arg) for arg in args if arg is not None and not isinstance(arg, bool)
3854        )
3855        if self.pretty and self.too_wide(arg_sqls):
3856            return self.indent(
3857                "\n" + f"{sep.strip()}\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True
3858            )
3859        return sep.join(arg_sqls)
3860
3861    def too_wide(self, args: t.Iterable) -> bool:
3862        return sum(len(arg) for arg in args) > self.max_text_width
3863
3864    def format_time(
3865        self,
3866        expression: exp.Expression,
3867        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
3868        inverse_time_trie: t.Optional[t.Dict] = None,
3869    ) -> t.Optional[str]:
3870        return format_time(
3871            self.sql(expression, "format"),
3872            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
3873            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
3874        )
3875
3876    def expressions(
3877        self,
3878        expression: t.Optional[exp.Expression] = None,
3879        key: t.Optional[str] = None,
3880        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
3881        flat: bool = False,
3882        indent: bool = True,
3883        skip_first: bool = False,
3884        skip_last: bool = False,
3885        sep: str = ", ",
3886        prefix: str = "",
3887        dynamic: bool = False,
3888        new_line: bool = False,
3889    ) -> str:
3890        expressions = expression.args.get(key or "expressions") if expression else sqls
3891
3892        if not expressions:
3893            return ""
3894
3895        if flat:
3896            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
3897
3898        num_sqls = len(expressions)
3899        result_sqls = []
3900
3901        for i, e in enumerate(expressions):
3902            sql = self.sql(e, comment=False)
3903            if not sql:
3904                continue
3905
3906            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
3907
3908            if self.pretty:
3909                if self.leading_comma:
3910                    result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}")
3911                else:
3912                    result_sqls.append(
3913                        f"{prefix}{sql}{(sep.rstrip() if comments else sep) if i + 1 < num_sqls else ''}{comments}"
3914                    )
3915            else:
3916                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
3917
3918        if self.pretty and (not dynamic or self.too_wide(result_sqls)):
3919            if new_line:
3920                result_sqls.insert(0, "")
3921                result_sqls.append("")
3922            result_sql = "\n".join(s.rstrip() for s in result_sqls)
3923        else:
3924            result_sql = "".join(result_sqls)
3925
3926        return (
3927            self.indent(result_sql, skip_first=skip_first, skip_last=skip_last)
3928            if indent
3929            else result_sql
3930        )
3931
3932    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
3933        flat = flat or isinstance(expression.parent, exp.Properties)
3934        expressions_sql = self.expressions(expression, flat=flat)
3935        if flat:
3936            return f"{op} {expressions_sql}"
3937        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
3938
3939    def naked_property(self, expression: exp.Property) -> str:
3940        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
3941        if not property_name:
3942            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
3943        return f"{property_name} {self.sql(expression, 'this')}"
3944
3945    def tag_sql(self, expression: exp.Tag) -> str:
3946        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
3947
3948    def token_sql(self, token_type: TokenType) -> str:
3949        return self.TOKEN_MAPPING.get(token_type, token_type.name)
3950
3951    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
3952        this = self.sql(expression, "this")
3953        expressions = self.no_identify(self.expressions, expression)
3954        expressions = (
3955            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
3956        )
3957        return f"{this}{expressions}" if expressions.strip() != "" else this
3958
3959    def joinhint_sql(self, expression: exp.JoinHint) -> str:
3960        this = self.sql(expression, "this")
3961        expressions = self.expressions(expression, flat=True)
3962        return f"{this}({expressions})"
3963
3964    def kwarg_sql(self, expression: exp.Kwarg) -> str:
3965        return self.binary(expression, "=>")
3966
3967    def when_sql(self, expression: exp.When) -> str:
3968        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
3969        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
3970        condition = self.sql(expression, "condition")
3971        condition = f" AND {condition}" if condition else ""
3972
3973        then_expression = expression.args.get("then")
3974        if isinstance(then_expression, exp.Insert):
3975            this = self.sql(then_expression, "this")
3976            this = f"INSERT {this}" if this else "INSERT"
3977            then = self.sql(then_expression, "expression")
3978            then = f"{this} VALUES {then}" if then else this
3979        elif isinstance(then_expression, exp.Update):
3980            if isinstance(then_expression.args.get("expressions"), exp.Star):
3981                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
3982            else:
3983                then = f"UPDATE SET{self.sep()}{self.expressions(then_expression)}"
3984        else:
3985            then = self.sql(then_expression)
3986        return f"WHEN {matched}{source}{condition} THEN {then}"
3987
3988    def whens_sql(self, expression: exp.Whens) -> str:
3989        return self.expressions(expression, sep=" ", indent=False)
3990
3991    def merge_sql(self, expression: exp.Merge) -> str:
3992        table = expression.this
3993        table_alias = ""
3994
3995        hints = table.args.get("hints")
3996        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
3997            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
3998            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
3999
4000        this = self.sql(table)
4001        using = f"USING {self.sql(expression, 'using')}"
4002        on = f"ON {self.sql(expression, 'on')}"
4003        whens = self.sql(expression, "whens")
4004
4005        returning = self.sql(expression, "returning")
4006        if returning:
4007            whens = f"{whens}{returning}"
4008
4009        sep = self.sep()
4010
4011        return self.prepend_ctes(
4012            expression,
4013            f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{whens}",
4014        )
4015
4016    @unsupported_args("format")
4017    def tochar_sql(self, expression: exp.ToChar) -> str:
4018        return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT))
4019
4020    def tonumber_sql(self, expression: exp.ToNumber) -> str:
4021        if not self.SUPPORTS_TO_NUMBER:
4022            self.unsupported("Unsupported TO_NUMBER function")
4023            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
4024
4025        fmt = expression.args.get("format")
4026        if not fmt:
4027            self.unsupported("Conversion format is required for TO_NUMBER")
4028            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
4029
4030        return self.func("TO_NUMBER", expression.this, fmt)
4031
4032    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
4033        this = self.sql(expression, "this")
4034        kind = self.sql(expression, "kind")
4035        settings_sql = self.expressions(expression, key="settings", sep=" ")
4036        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
4037        return f"{this}({kind}{args})"
4038
4039    def dictrange_sql(self, expression: exp.DictRange) -> str:
4040        this = self.sql(expression, "this")
4041        max = self.sql(expression, "max")
4042        min = self.sql(expression, "min")
4043        return f"{this}(MIN {min} MAX {max})"
4044
4045    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
4046        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
4047
4048    def duplicatekeyproperty_sql(self, expression: exp.DuplicateKeyProperty) -> str:
4049        return f"DUPLICATE KEY ({self.expressions(expression, flat=True)})"
4050
4051    # https://docs.starrocks.io/docs/sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE/
4052    def uniquekeyproperty_sql(
4053        self, expression: exp.UniqueKeyProperty, prefix: str = "UNIQUE KEY"
4054    ) -> str:
4055        return f"{prefix} ({self.expressions(expression, flat=True)})"
4056
4057    # https://docs.starrocks.io/docs/sql-reference/sql-statements/data-definition/CREATE_TABLE/#distribution_desc
4058    def distributedbyproperty_sql(self, expression: exp.DistributedByProperty) -> str:
4059        expressions = self.expressions(expression, flat=True)
4060        expressions = f" {self.wrap(expressions)}" if expressions else ""
4061        buckets = self.sql(expression, "buckets")
4062        kind = self.sql(expression, "kind")
4063        buckets = f" BUCKETS {buckets}" if buckets else ""
4064        order = self.sql(expression, "order")
4065        return f"DISTRIBUTED BY {kind}{expressions}{buckets}{order}"
4066
4067    def oncluster_sql(self, expression: exp.OnCluster) -> str:
4068        return ""
4069
4070    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
4071        expressions = self.expressions(expression, key="expressions", flat=True)
4072        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
4073        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
4074        buckets = self.sql(expression, "buckets")
4075        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
4076
4077    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
4078        this = self.sql(expression, "this")
4079        having = self.sql(expression, "having")
4080
4081        if having:
4082            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
4083
4084        return self.func("ANY_VALUE", this)
4085
4086    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
4087        transform = self.func("TRANSFORM", *expression.expressions)
4088        row_format_before = self.sql(expression, "row_format_before")
4089        row_format_before = f" {row_format_before}" if row_format_before else ""
4090        record_writer = self.sql(expression, "record_writer")
4091        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
4092        using = f" USING {self.sql(expression, 'command_script')}"
4093        schema = self.sql(expression, "schema")
4094        schema = f" AS {schema}" if schema else ""
4095        row_format_after = self.sql(expression, "row_format_after")
4096        row_format_after = f" {row_format_after}" if row_format_after else ""
4097        record_reader = self.sql(expression, "record_reader")
4098        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
4099        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
4100
4101    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
4102        key_block_size = self.sql(expression, "key_block_size")
4103        if key_block_size:
4104            return f"KEY_BLOCK_SIZE = {key_block_size}"
4105
4106        using = self.sql(expression, "using")
4107        if using:
4108            return f"USING {using}"
4109
4110        parser = self.sql(expression, "parser")
4111        if parser:
4112            return f"WITH PARSER {parser}"
4113
4114        comment = self.sql(expression, "comment")
4115        if comment:
4116            return f"COMMENT {comment}"
4117
4118        visible = expression.args.get("visible")
4119        if visible is not None:
4120            return "VISIBLE" if visible else "INVISIBLE"
4121
4122        engine_attr = self.sql(expression, "engine_attr")
4123        if engine_attr:
4124            return f"ENGINE_ATTRIBUTE = {engine_attr}"
4125
4126        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
4127        if secondary_engine_attr:
4128            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
4129
4130        self.unsupported("Unsupported index constraint option.")
4131        return ""
4132
4133    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
4134        enforced = " ENFORCED" if expression.args.get("enforced") else ""
4135        return f"CHECK ({self.sql(expression, 'this')}){enforced}"
4136
4137    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
4138        kind = self.sql(expression, "kind")
4139        kind = f"{kind} INDEX" if kind else "INDEX"
4140        this = self.sql(expression, "this")
4141        this = f" {this}" if this else ""
4142        index_type = self.sql(expression, "index_type")
4143        index_type = f" USING {index_type}" if index_type else ""
4144        expressions = self.expressions(expression, flat=True)
4145        expressions = f" ({expressions})" if expressions else ""
4146        options = self.expressions(expression, key="options", sep=" ")
4147        options = f" {options}" if options else ""
4148        return f"{kind}{this}{index_type}{expressions}{options}"
4149
4150    def nvl2_sql(self, expression: exp.Nvl2) -> str:
4151        if self.NVL2_SUPPORTED:
4152            return self.function_fallback_sql(expression)
4153
4154        case = exp.Case().when(
4155            expression.this.is_(exp.null()).not_(copy=False),
4156            expression.args["true"],
4157            copy=False,
4158        )
4159        else_cond = expression.args.get("false")
4160        if else_cond:
4161            case.else_(else_cond, copy=False)
4162
4163        return self.sql(case)
4164
4165    def comprehension_sql(self, expression: exp.Comprehension) -> str:
4166        this = self.sql(expression, "this")
4167        expr = self.sql(expression, "expression")
4168        iterator = self.sql(expression, "iterator")
4169        condition = self.sql(expression, "condition")
4170        condition = f" IF {condition}" if condition else ""
4171        return f"{this} FOR {expr} IN {iterator}{condition}"
4172
4173    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
4174        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
4175
4176    def opclass_sql(self, expression: exp.Opclass) -> str:
4177        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
4178
4179    def predict_sql(self, expression: exp.Predict) -> str:
4180        model = self.sql(expression, "this")
4181        model = f"MODEL {model}"
4182        table = self.sql(expression, "expression")
4183        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
4184        parameters = self.sql(expression, "params_struct")
4185        return self.func("PREDICT", model, table, parameters or None)
4186
4187    def generateembedding_sql(self, expression: exp.GenerateEmbedding) -> str:
4188        model = self.sql(expression, "this")
4189        model = f"MODEL {model}"
4190        table = self.sql(expression, "expression")
4191        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
4192        parameters = self.sql(expression, "params_struct")
4193        return self.func("GENERATE_EMBEDDING", model, table, parameters or None)
4194
4195    def featuresattime_sql(self, expression: exp.FeaturesAtTime) -> str:
4196        this_sql = self.sql(expression, "this")
4197        if isinstance(expression.this, exp.Table):
4198            this_sql = f"TABLE {this_sql}"
4199
4200        return self.func(
4201            "FEATURES_AT_TIME",
4202            this_sql,
4203            expression.args.get("time"),
4204            expression.args.get("num_rows"),
4205            expression.args.get("ignore_feature_nulls"),
4206        )
4207
4208    def vectorsearch_sql(self, expression: exp.VectorSearch) -> str:
4209        this_sql = self.sql(expression, "this")
4210        if isinstance(expression.this, exp.Table):
4211            this_sql = f"TABLE {this_sql}"
4212
4213        query_table = self.sql(expression, "query_table")
4214        if isinstance(expression.args["query_table"], exp.Table):
4215            query_table = f"TABLE {query_table}"
4216
4217        return self.func(
4218            "VECTOR_SEARCH",
4219            this_sql,
4220            expression.args.get("column_to_search"),
4221            query_table,
4222            expression.args.get("query_column_to_search"),
4223            expression.args.get("top_k"),
4224            expression.args.get("distance_type"),
4225            expression.args.get("options"),
4226        )
4227
4228    def forin_sql(self, expression: exp.ForIn) -> str:
4229        this = self.sql(expression, "this")
4230        expression_sql = self.sql(expression, "expression")
4231        return f"FOR {this} DO {expression_sql}"
4232
4233    def refresh_sql(self, expression: exp.Refresh) -> str:
4234        this = self.sql(expression, "this")
4235        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
4236        return f"REFRESH {table}{this}"
4237
4238    def toarray_sql(self, expression: exp.ToArray) -> str:
4239        arg = expression.this
4240        if not arg.type:
4241            from sqlglot.optimizer.annotate_types import annotate_types
4242
4243            arg = annotate_types(arg, dialect=self.dialect)
4244
4245        if arg.is_type(exp.DataType.Type.ARRAY):
4246            return self.sql(arg)
4247
4248        cond_for_null = arg.is_(exp.null())
4249        return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
4250
4251    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
4252        this = expression.this
4253        time_format = self.format_time(expression)
4254
4255        if time_format:
4256            return self.sql(
4257                exp.cast(
4258                    exp.StrToTime(this=this, format=expression.args["format"]),
4259                    exp.DataType.Type.TIME,
4260                )
4261            )
4262
4263        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
4264            return self.sql(this)
4265
4266        return self.sql(exp.cast(this, exp.DataType.Type.TIME))
4267
4268    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
4269        this = expression.this
4270        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
4271            return self.sql(this)
4272
4273        return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP, dialect=self.dialect))
4274
4275    def tsordstodatetime_sql(self, expression: exp.TsOrDsToDatetime) -> str:
4276        this = expression.this
4277        if isinstance(this, exp.TsOrDsToDatetime) or this.is_type(exp.DataType.Type.DATETIME):
4278            return self.sql(this)
4279
4280        return self.sql(exp.cast(this, exp.DataType.Type.DATETIME, dialect=self.dialect))
4281
4282    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
4283        this = expression.this
4284        time_format = self.format_time(expression)
4285
4286        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
4287            return self.sql(
4288                exp.cast(
4289                    exp.StrToTime(this=this, format=expression.args["format"]),
4290                    exp.DataType.Type.DATE,
4291                )
4292            )
4293
4294        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
4295            return self.sql(this)
4296
4297        return self.sql(exp.cast(this, exp.DataType.Type.DATE))
4298
4299    def unixdate_sql(self, expression: exp.UnixDate) -> str:
4300        return self.sql(
4301            exp.func(
4302                "DATEDIFF",
4303                expression.this,
4304                exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
4305                "day",
4306            )
4307        )
4308
4309    def lastday_sql(self, expression: exp.LastDay) -> str:
4310        if self.LAST_DAY_SUPPORTS_DATE_PART:
4311            return self.function_fallback_sql(expression)
4312
4313        unit = expression.text("unit")
4314        if unit and unit != "MONTH":
4315            self.unsupported("Date parts are not supported in LAST_DAY.")
4316
4317        return self.func("LAST_DAY", expression.this)
4318
4319    def dateadd_sql(self, expression: exp.DateAdd) -> str:
4320        from sqlglot.dialects.dialect import unit_to_str
4321
4322        return self.func(
4323            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
4324        )
4325
4326    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
4327        if self.CAN_IMPLEMENT_ARRAY_ANY:
4328            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
4329            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
4330            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
4331            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
4332
4333        from sqlglot.dialects import Dialect
4334
4335        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
4336        if self.dialect.__class__ != Dialect:
4337            self.unsupported("ARRAY_ANY is unsupported")
4338
4339        return self.function_fallback_sql(expression)
4340
4341    def struct_sql(self, expression: exp.Struct) -> str:
4342        expression.set(
4343            "expressions",
4344            [
4345                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
4346                if isinstance(e, exp.PropertyEQ)
4347                else e
4348                for e in expression.expressions
4349            ],
4350        )
4351
4352        return self.function_fallback_sql(expression)
4353
4354    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
4355        low = self.sql(expression, "this")
4356        high = self.sql(expression, "expression")
4357
4358        return f"{low} TO {high}"
4359
4360    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
4361        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
4362        tables = f" {self.expressions(expression)}"
4363
4364        exists = " IF EXISTS" if expression.args.get("exists") else ""
4365
4366        on_cluster = self.sql(expression, "cluster")
4367        on_cluster = f" {on_cluster}" if on_cluster else ""
4368
4369        identity = self.sql(expression, "identity")
4370        identity = f" {identity} IDENTITY" if identity else ""
4371
4372        option = self.sql(expression, "option")
4373        option = f" {option}" if option else ""
4374
4375        partition = self.sql(expression, "partition")
4376        partition = f" {partition}" if partition else ""
4377
4378        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
4379
4380    # This transpiles T-SQL's CONVERT function
4381    # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16
4382    def convert_sql(self, expression: exp.Convert) -> str:
4383        to = expression.this
4384        value = expression.expression
4385        style = expression.args.get("style")
4386        safe = expression.args.get("safe")
4387        strict = expression.args.get("strict")
4388
4389        if not to or not value:
4390            return ""
4391
4392        # Retrieve length of datatype and override to default if not specified
4393        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
4394            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
4395
4396        transformed: t.Optional[exp.Expression] = None
4397        cast = exp.Cast if strict else exp.TryCast
4398
4399        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
4400        if isinstance(style, exp.Literal) and style.is_int:
4401            from sqlglot.dialects.tsql import TSQL
4402
4403            style_value = style.name
4404            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
4405            if not converted_style:
4406                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
4407
4408            fmt = exp.Literal.string(converted_style)
4409
4410            if to.this == exp.DataType.Type.DATE:
4411                transformed = exp.StrToDate(this=value, format=fmt)
4412            elif to.this in (exp.DataType.Type.DATETIME, exp.DataType.Type.DATETIME2):
4413                transformed = exp.StrToTime(this=value, format=fmt)
4414            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
4415                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
4416            elif to.this == exp.DataType.Type.TEXT:
4417                transformed = exp.TimeToStr(this=value, format=fmt)
4418
4419        if not transformed:
4420            transformed = cast(this=value, to=to, safe=safe)
4421
4422        return self.sql(transformed)
4423
4424    def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str:
4425        this = expression.this
4426        if isinstance(this, exp.JSONPathWildcard):
4427            this = self.json_path_part(this)
4428            return f".{this}" if this else ""
4429
4430        if self.SAFE_JSON_PATH_KEY_RE.match(this):
4431            return f".{this}"
4432
4433        this = self.json_path_part(this)
4434        return (
4435            f"[{this}]"
4436            if self._quote_json_path_key_using_brackets and self.JSON_PATH_BRACKETED_KEY_SUPPORTED
4437            else f".{this}"
4438        )
4439
4440    def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str:
4441        this = self.json_path_part(expression.this)
4442        return f"[{this}]" if this else ""
4443
4444    def _simplify_unless_literal(self, expression: E) -> E:
4445        if not isinstance(expression, exp.Literal):
4446            from sqlglot.optimizer.simplify import simplify
4447
4448            expression = simplify(expression, dialect=self.dialect)
4449
4450        return expression
4451
4452    def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str:
4453        this = expression.this
4454        if isinstance(this, self.RESPECT_IGNORE_NULLS_UNSUPPORTED_EXPRESSIONS):
4455            self.unsupported(
4456                f"RESPECT/IGNORE NULLS is not supported for {type(this).key} in {self.dialect.__class__.__name__}"
4457            )
4458            return self.sql(this)
4459
4460        if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"):
4461            # The first modifier here will be the one closest to the AggFunc's arg
4462            mods = sorted(
4463                expression.find_all(exp.HavingMax, exp.Order, exp.Limit),
4464                key=lambda x: 0
4465                if isinstance(x, exp.HavingMax)
4466                else (1 if isinstance(x, exp.Order) else 2),
4467            )
4468
4469            if mods:
4470                mod = mods[0]
4471                this = expression.__class__(this=mod.this.copy())
4472                this.meta["inline"] = True
4473                mod.this.replace(this)
4474                return self.sql(expression.this)
4475
4476            agg_func = expression.find(exp.AggFunc)
4477
4478            if agg_func:
4479                agg_func_sql = self.sql(agg_func, comment=False)[:-1] + f" {text})"
4480                return self.maybe_comment(agg_func_sql, comments=agg_func.comments)
4481
4482        return f"{self.sql(expression, 'this')} {text}"
4483
4484    def _replace_line_breaks(self, string: str) -> str:
4485        """We don't want to extra indent line breaks so we temporarily replace them with sentinels."""
4486        if self.pretty:
4487            return string.replace("\n", self.SENTINEL_LINE_BREAK)
4488        return string
4489
4490    def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
4491        option = self.sql(expression, "this")
4492
4493        if expression.expressions:
4494            upper = option.upper()
4495
4496            # Snowflake FILE_FORMAT options are separated by whitespace
4497            sep = " " if upper == "FILE_FORMAT" else ", "
4498
4499            # Databricks copy/format options do not set their list of values with EQ
4500            op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = "
4501            values = self.expressions(expression, flat=True, sep=sep)
4502            return f"{option}{op}({values})"
4503
4504        value = self.sql(expression, "expression")
4505
4506        if not value:
4507            return option
4508
4509        op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " "
4510
4511        return f"{option}{op}{value}"
4512
4513    def credentials_sql(self, expression: exp.Credentials) -> str:
4514        cred_expr = expression.args.get("credentials")
4515        if isinstance(cred_expr, exp.Literal):
4516            # Redshift case: CREDENTIALS <string>
4517            credentials = self.sql(expression, "credentials")
4518            credentials = f"CREDENTIALS {credentials}" if credentials else ""
4519        else:
4520            # Snowflake case: CREDENTIALS = (...)
4521            credentials = self.expressions(expression, key="credentials", flat=True, sep=" ")
4522            credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else ""
4523
4524        storage = self.sql(expression, "storage")
4525        storage = f"STORAGE_INTEGRATION = {storage}" if storage else ""
4526
4527        encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
4528        encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
4529
4530        iam_role = self.sql(expression, "iam_role")
4531        iam_role = f"IAM_ROLE {iam_role}" if iam_role else ""
4532
4533        region = self.sql(expression, "region")
4534        region = f" REGION {region}" if region else ""
4535
4536        return f"{credentials}{storage}{encryption}{iam_role}{region}"
4537
4538    def copy_sql(self, expression: exp.Copy) -> str:
4539        this = self.sql(expression, "this")
4540        this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
4541
4542        credentials = self.sql(expression, "credentials")
4543        credentials = self.seg(credentials) if credentials else ""
4544        kind = self.seg("FROM" if expression.args.get("kind") else "TO")
4545        files = self.expressions(expression, key="files", flat=True)
4546
4547        sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
4548        params = self.expressions(
4549            expression,
4550            key="params",
4551            sep=sep,
4552            new_line=True,
4553            skip_last=True,
4554            skip_first=True,
4555            indent=self.COPY_PARAMS_ARE_WRAPPED,
4556        )
4557
4558        if params:
4559            if self.COPY_PARAMS_ARE_WRAPPED:
4560                params = f" WITH ({params})"
4561            elif not self.pretty:
4562                params = f" {params}"
4563
4564        return f"COPY{this}{kind} {files}{credentials}{params}"
4565
4566    def semicolon_sql(self, expression: exp.Semicolon) -> str:
4567        return ""
4568
4569    def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
4570        on_sql = "ON" if expression.args.get("on") else "OFF"
4571        filter_col: t.Optional[str] = self.sql(expression, "filter_column")
4572        filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
4573        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
4574        retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
4575
4576        if filter_col or retention_period:
4577            on_sql = self.func("ON", filter_col, retention_period)
4578
4579        return f"DATA_DELETION={on_sql}"
4580
4581    def maskingpolicycolumnconstraint_sql(
4582        self, expression: exp.MaskingPolicyColumnConstraint
4583    ) -> str:
4584        this = self.sql(expression, "this")
4585        expressions = self.expressions(expression, flat=True)
4586        expressions = f" USING ({expressions})" if expressions else ""
4587        return f"MASKING POLICY {this}{expressions}"
4588
4589    def gapfill_sql(self, expression: exp.GapFill) -> str:
4590        this = self.sql(expression, "this")
4591        this = f"TABLE {this}"
4592        return self.func("GAP_FILL", this, *[v for k, v in expression.args.items() if k != "this"])
4593
4594    def scope_resolution(self, rhs: str, scope_name: str) -> str:
4595        return self.func("SCOPE_RESOLUTION", scope_name or None, rhs)
4596
4597    def scoperesolution_sql(self, expression: exp.ScopeResolution) -> str:
4598        this = self.sql(expression, "this")
4599        expr = expression.expression
4600
4601        if isinstance(expr, exp.Func):
4602            # T-SQL's CLR functions are case sensitive
4603            expr = f"{self.sql(expr, 'this')}({self.format_args(*expr.expressions)})"
4604        else:
4605            expr = self.sql(expression, "expression")
4606
4607        return self.scope_resolution(expr, this)
4608
4609    def parsejson_sql(self, expression: exp.ParseJSON) -> str:
4610        if self.PARSE_JSON_NAME is None:
4611            return self.sql(expression.this)
4612
4613        return self.func(self.PARSE_JSON_NAME, expression.this, expression.expression)
4614
4615    def rand_sql(self, expression: exp.Rand) -> str:
4616        lower = self.sql(expression, "lower")
4617        upper = self.sql(expression, "upper")
4618
4619        if lower and upper:
4620            return f"({upper} - {lower}) * {self.func('RAND', expression.this)} + {lower}"
4621        return self.func("RAND", expression.this)
4622
4623    def changes_sql(self, expression: exp.Changes) -> str:
4624        information = self.sql(expression, "information")
4625        information = f"INFORMATION => {information}"
4626        at_before = self.sql(expression, "at_before")
4627        at_before = f"{self.seg('')}{at_before}" if at_before else ""
4628        end = self.sql(expression, "end")
4629        end = f"{self.seg('')}{end}" if end else ""
4630
4631        return f"CHANGES ({information}){at_before}{end}"
4632
4633    def pad_sql(self, expression: exp.Pad) -> str:
4634        prefix = "L" if expression.args.get("is_left") else "R"
4635
4636        fill_pattern = self.sql(expression, "fill_pattern") or None
4637        if not fill_pattern and self.PAD_FILL_PATTERN_IS_REQUIRED:
4638            fill_pattern = "' '"
4639
4640        return self.func(f"{prefix}PAD", expression.this, expression.expression, fill_pattern)
4641
4642    def summarize_sql(self, expression: exp.Summarize) -> str:
4643        table = " TABLE" if expression.args.get("table") else ""
4644        return f"SUMMARIZE{table} {self.sql(expression.this)}"
4645
4646    def explodinggenerateseries_sql(self, expression: exp.ExplodingGenerateSeries) -> str:
4647        generate_series = exp.GenerateSeries(**expression.args)
4648
4649        parent = expression.parent
4650        if isinstance(parent, (exp.Alias, exp.TableAlias)):
4651            parent = parent.parent
4652
4653        if self.SUPPORTS_EXPLODING_PROJECTIONS and not isinstance(parent, (exp.Table, exp.Unnest)):
4654            return self.sql(exp.Unnest(expressions=[generate_series]))
4655
4656        if isinstance(parent, exp.Select):
4657            self.unsupported("GenerateSeries projection unnesting is not supported.")
4658
4659        return self.sql(generate_series)
4660
4661    def arrayconcat_sql(self, expression: exp.ArrayConcat, name: str = "ARRAY_CONCAT") -> str:
4662        exprs = expression.expressions
4663        if not self.ARRAY_CONCAT_IS_VAR_LEN:
4664            rhs = reduce(lambda x, y: exp.ArrayConcat(this=x, expressions=[y]), exprs)
4665        else:
4666            rhs = self.expressions(expression)
4667
4668        return self.func(name, expression.this, rhs or None)
4669
4670    def converttimezone_sql(self, expression: exp.ConvertTimezone) -> str:
4671        if self.SUPPORTS_CONVERT_TIMEZONE:
4672            return self.function_fallback_sql(expression)
4673
4674        source_tz = expression.args.get("source_tz")
4675        target_tz = expression.args.get("target_tz")
4676        timestamp = expression.args.get("timestamp")
4677
4678        if source_tz and timestamp:
4679            timestamp = exp.AtTimeZone(
4680                this=exp.cast(timestamp, exp.DataType.Type.TIMESTAMPNTZ), zone=source_tz
4681            )
4682
4683        expr = exp.AtTimeZone(this=timestamp, zone=target_tz)
4684
4685        return self.sql(expr)
4686
4687    def json_sql(self, expression: exp.JSON) -> str:
4688        this = self.sql(expression, "this")
4689        this = f" {this}" if this else ""
4690
4691        _with = expression.args.get("with")
4692
4693        if _with is None:
4694            with_sql = ""
4695        elif not _with:
4696            with_sql = " WITHOUT"
4697        else:
4698            with_sql = " WITH"
4699
4700        unique_sql = " UNIQUE KEYS" if expression.args.get("unique") else ""
4701
4702        return f"JSON{this}{with_sql}{unique_sql}"
4703
4704    def jsonvalue_sql(self, expression: exp.JSONValue) -> str:
4705        def _generate_on_options(arg: t.Any) -> str:
4706            return arg if isinstance(arg, str) else f"DEFAULT {self.sql(arg)}"
4707
4708        path = self.sql(expression, "path")
4709        returning = self.sql(expression, "returning")
4710        returning = f" RETURNING {returning}" if returning else ""
4711
4712        on_condition = self.sql(expression, "on_condition")
4713        on_condition = f" {on_condition}" if on_condition else ""
4714
4715        return self.func("JSON_VALUE", expression.this, f"{path}{returning}{on_condition}")
4716
4717    def conditionalinsert_sql(self, expression: exp.ConditionalInsert) -> str:
4718        else_ = "ELSE " if expression.args.get("else_") else ""
4719        condition = self.sql(expression, "expression")
4720        condition = f"WHEN {condition} THEN " if condition else else_
4721        insert = self.sql(expression, "this")[len("INSERT") :].strip()
4722        return f"{condition}{insert}"
4723
4724    def multitableinserts_sql(self, expression: exp.MultitableInserts) -> str:
4725        kind = self.sql(expression, "kind")
4726        expressions = self.seg(self.expressions(expression, sep=" "))
4727        res = f"INSERT {kind}{expressions}{self.seg(self.sql(expression, 'source'))}"
4728        return res
4729
4730    def oncondition_sql(self, expression: exp.OnCondition) -> str:
4731        # Static options like "NULL ON ERROR" are stored as strings, in contrast to "DEFAULT <expr> ON ERROR"
4732        empty = expression.args.get("empty")
4733        empty = (
4734            f"DEFAULT {empty} ON EMPTY"
4735            if isinstance(empty, exp.Expression)
4736            else self.sql(expression, "empty")
4737        )
4738
4739        error = expression.args.get("error")
4740        error = (
4741            f"DEFAULT {error} ON ERROR"
4742            if isinstance(error, exp.Expression)
4743            else self.sql(expression, "error")
4744        )
4745
4746        if error and empty:
4747            error = (
4748                f"{empty} {error}"
4749                if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR
4750                else f"{error} {empty}"
4751            )
4752            empty = ""
4753
4754        null = self.sql(expression, "null")
4755
4756        return f"{empty}{error}{null}"
4757
4758    def jsonextractquote_sql(self, expression: exp.JSONExtractQuote) -> str:
4759        scalar = " ON SCALAR STRING" if expression.args.get("scalar") else ""
4760        return f"{self.sql(expression, 'option')} QUOTES{scalar}"
4761
4762    def jsonexists_sql(self, expression: exp.JSONExists) -> str:
4763        this = self.sql(expression, "this")
4764        path = self.sql(expression, "path")
4765
4766        passing = self.expressions(expression, "passing")
4767        passing = f" PASSING {passing}" if passing else ""
4768
4769        on_condition = self.sql(expression, "on_condition")
4770        on_condition = f" {on_condition}" if on_condition else ""
4771
4772        path = f"{path}{passing}{on_condition}"
4773
4774        return self.func("JSON_EXISTS", this, path)
4775
4776    def arrayagg_sql(self, expression: exp.ArrayAgg) -> str:
4777        array_agg = self.function_fallback_sql(expression)
4778
4779        # Add a NULL FILTER on the column to mimic the results going from a dialect that excludes nulls
4780        # on ARRAY_AGG (e.g Spark) to one that doesn't (e.g. DuckDB)
4781        if self.dialect.ARRAY_AGG_INCLUDES_NULLS and expression.args.get("nulls_excluded"):
4782            parent = expression.parent
4783            if isinstance(parent, exp.Filter):
4784                parent_cond = parent.expression.this
4785                parent_cond.replace(parent_cond.and_(expression.this.is_(exp.null()).not_()))
4786            else:
4787                this = expression.this
4788                # Do not add the filter if the input is not a column (e.g. literal, struct etc)
4789                if this.find(exp.Column):
4790                    # DISTINCT is already present in the agg function, do not propagate it to FILTER as well
4791                    this_sql = (
4792                        self.expressions(this)
4793                        if isinstance(this, exp.Distinct)
4794                        else self.sql(expression, "this")
4795                    )
4796
4797                    array_agg = f"{array_agg} FILTER(WHERE {this_sql} IS NOT NULL)"
4798
4799        return array_agg
4800
4801    def apply_sql(self, expression: exp.Apply) -> str:
4802        this = self.sql(expression, "this")
4803        expr = self.sql(expression, "expression")
4804
4805        return f"{this} APPLY({expr})"
4806
4807    def grant_sql(self, expression: exp.Grant) -> str:
4808        privileges_sql = self.expressions(expression, key="privileges", flat=True)
4809
4810        kind = self.sql(expression, "kind")
4811        kind = f" {kind}" if kind else ""
4812
4813        securable = self.sql(expression, "securable")
4814        securable = f" {securable}" if securable else ""
4815
4816        principals = self.expressions(expression, key="principals", flat=True)
4817
4818        grant_option = " WITH GRANT OPTION" if expression.args.get("grant_option") else ""
4819
4820        return f"GRANT {privileges_sql} ON{kind}{securable} TO {principals}{grant_option}"
4821
4822    def grantprivilege_sql(self, expression: exp.GrantPrivilege):
4823        this = self.sql(expression, "this")
4824        columns = self.expressions(expression, flat=True)
4825        columns = f"({columns})" if columns else ""
4826
4827        return f"{this}{columns}"
4828
4829    def grantprincipal_sql(self, expression: exp.GrantPrincipal):
4830        this = self.sql(expression, "this")
4831
4832        kind = self.sql(expression, "kind")
4833        kind = f"{kind} " if kind else ""
4834
4835        return f"{kind}{this}"
4836
4837    def columns_sql(self, expression: exp.Columns):
4838        func = self.function_fallback_sql(expression)
4839        if expression.args.get("unpack"):
4840            func = f"*{func}"
4841
4842        return func
4843
4844    def overlay_sql(self, expression: exp.Overlay):
4845        this = self.sql(expression, "this")
4846        expr = self.sql(expression, "expression")
4847        from_sql = self.sql(expression, "from")
4848        for_sql = self.sql(expression, "for")
4849        for_sql = f" FOR {for_sql}" if for_sql else ""
4850
4851        return f"OVERLAY({this} PLACING {expr} FROM {from_sql}{for_sql})"
4852
4853    @unsupported_args("format")
4854    def todouble_sql(self, expression: exp.ToDouble) -> str:
4855        return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
4856
4857    def string_sql(self, expression: exp.String) -> str:
4858        this = expression.this
4859        zone = expression.args.get("zone")
4860
4861        if zone:
4862            # This is a BigQuery specific argument for STRING(<timestamp_expr>, <time_zone>)
4863            # BigQuery stores timestamps internally as UTC, so ConvertTimezone is used with UTC
4864            # set for source_tz to transpile the time conversion before the STRING cast
4865            this = exp.ConvertTimezone(
4866                source_tz=exp.Literal.string("UTC"), target_tz=zone, timestamp=this
4867            )
4868
4869        return self.sql(exp.cast(this, exp.DataType.Type.VARCHAR))
4870
4871    def median_sql(self, expression: exp.Median):
4872        if not self.SUPPORTS_MEDIAN:
4873            return self.sql(
4874                exp.PercentileCont(this=expression.this, expression=exp.Literal.number(0.5))
4875            )
4876
4877        return self.function_fallback_sql(expression)
4878
4879    def overflowtruncatebehavior_sql(self, expression: exp.OverflowTruncateBehavior) -> str:
4880        filler = self.sql(expression, "this")
4881        filler = f" {filler}" if filler else ""
4882        with_count = "WITH COUNT" if expression.args.get("with_count") else "WITHOUT COUNT"
4883        return f"TRUNCATE{filler} {with_count}"
4884
4885    def unixseconds_sql(self, expression: exp.UnixSeconds) -> str:
4886        if self.SUPPORTS_UNIX_SECONDS:
4887            return self.function_fallback_sql(expression)
4888
4889        start_ts = exp.cast(
4890            exp.Literal.string("1970-01-01 00:00:00+00"), to=exp.DataType.Type.TIMESTAMPTZ
4891        )
4892
4893        return self.sql(
4894            exp.TimestampDiff(this=expression.this, expression=start_ts, unit=exp.var("SECONDS"))
4895        )
4896
4897    def arraysize_sql(self, expression: exp.ArraySize) -> str:
4898        dim = expression.expression
4899
4900        # For dialects that don't support the dimension arg, we can safely transpile it's default value (1st dimension)
4901        if dim and self.ARRAY_SIZE_DIM_REQUIRED is None:
4902            if not (dim.is_int and dim.name == "1"):
4903                self.unsupported("Cannot transpile dimension argument for ARRAY_LENGTH")
4904            dim = None
4905
4906        # If dimension is required but not specified, default initialize it
4907        if self.ARRAY_SIZE_DIM_REQUIRED and not dim:
4908            dim = exp.Literal.number(1)
4909
4910        return self.func(self.ARRAY_SIZE_NAME, expression.this, dim)
4911
4912    def attach_sql(self, expression: exp.Attach) -> str:
4913        this = self.sql(expression, "this")
4914        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
4915        expressions = self.expressions(expression)
4916        expressions = f" ({expressions})" if expressions else ""
4917
4918        return f"ATTACH{exists_sql} {this}{expressions}"
4919
4920    def detach_sql(self, expression: exp.Detach) -> str:
4921        this = self.sql(expression, "this")
4922        # the DATABASE keyword is required if IF EXISTS is set
4923        # without it, DuckDB throws an error: Parser Error: syntax error at or near "exists" (Line Number: 1)
4924        # ref: https://duckdb.org/docs/stable/sql/statements/attach.html#detach-syntax
4925        exists_sql = " DATABASE IF EXISTS" if expression.args.get("exists") else ""
4926
4927        return f"DETACH{exists_sql} {this}"
4928
4929    def attachoption_sql(self, expression: exp.AttachOption) -> str:
4930        this = self.sql(expression, "this")
4931        value = self.sql(expression, "expression")
4932        value = f" {value}" if value else ""
4933        return f"{this}{value}"
4934
4935    def watermarkcolumnconstraint_sql(self, expression: exp.WatermarkColumnConstraint) -> str:
4936        return (
4937            f"WATERMARK FOR {self.sql(expression, 'this')} AS {self.sql(expression, 'expression')}"
4938        )
4939
4940    def encodeproperty_sql(self, expression: exp.EncodeProperty) -> str:
4941        encode = "KEY ENCODE" if expression.args.get("key") else "ENCODE"
4942        encode = f"{encode} {self.sql(expression, 'this')}"
4943
4944        properties = expression.args.get("properties")
4945        if properties:
4946            encode = f"{encode} {self.properties(properties)}"
4947
4948        return encode
4949
4950    def includeproperty_sql(self, expression: exp.IncludeProperty) -> str:
4951        this = self.sql(expression, "this")
4952        include = f"INCLUDE {this}"
4953
4954        column_def = self.sql(expression, "column_def")
4955        if column_def:
4956            include = f"{include} {column_def}"
4957
4958        alias = self.sql(expression, "alias")
4959        if alias:
4960            include = f"{include} AS {alias}"
4961
4962        return include
4963
4964    def xmlelement_sql(self, expression: exp.XMLElement) -> str:
4965        name = f"NAME {self.sql(expression, 'this')}"
4966        return self.func("XMLELEMENT", name, *expression.expressions)
4967
4968    def xmlkeyvalueoption_sql(self, expression: exp.XMLKeyValueOption) -> str:
4969        this = self.sql(expression, "this")
4970        expr = self.sql(expression, "expression")
4971        expr = f"({expr})" if expr else ""
4972        return f"{this}{expr}"
4973
4974    def partitionbyrangeproperty_sql(self, expression: exp.PartitionByRangeProperty) -> str:
4975        partitions = self.expressions(expression, "partition_expressions")
4976        create = self.expressions(expression, "create_expressions")
4977        return f"PARTITION BY RANGE {self.wrap(partitions)} {self.wrap(create)}"
4978
4979    def partitionbyrangepropertydynamic_sql(
4980        self, expression: exp.PartitionByRangePropertyDynamic
4981    ) -> str:
4982        start = self.sql(expression, "start")
4983        end = self.sql(expression, "end")
4984
4985        every = expression.args["every"]
4986        if isinstance(every, exp.Interval) and every.this.is_string:
4987            every.this.replace(exp.Literal.number(every.name))
4988
4989        return f"START {self.wrap(start)} END {self.wrap(end)} EVERY {self.wrap(self.sql(every))}"
4990
4991    def unpivotcolumns_sql(self, expression: exp.UnpivotColumns) -> str:
4992        name = self.sql(expression, "this")
4993        values = self.expressions(expression, flat=True)
4994
4995        return f"NAME {name} VALUE {values}"
4996
4997    def analyzesample_sql(self, expression: exp.AnalyzeSample) -> str:
4998        kind = self.sql(expression, "kind")
4999        sample = self.sql(expression, "sample")
5000        return f"SAMPLE {sample} {kind}"
5001
5002    def analyzestatistics_sql(self, expression: exp.AnalyzeStatistics) -> str:
5003        kind = self.sql(expression, "kind")
5004        option = self.sql(expression, "option")
5005        option = f" {option}" if option else ""
5006        this = self.sql(expression, "this")
5007        this = f" {this}" if this else ""
5008        columns = self.expressions(expression)
5009        columns = f" {columns}" if columns else ""
5010        return f"{kind}{option} STATISTICS{this}{columns}"
5011
5012    def analyzehistogram_sql(self, expression: exp.AnalyzeHistogram) -> str:
5013        this = self.sql(expression, "this")
5014        columns = self.expressions(expression)
5015        inner_expression = self.sql(expression, "expression")
5016        inner_expression = f" {inner_expression}" if inner_expression else ""
5017        update_options = self.sql(expression, "update_options")
5018        update_options = f" {update_options} UPDATE" if update_options else ""
5019        return f"{this} HISTOGRAM ON {columns}{inner_expression}{update_options}"
5020
5021    def analyzedelete_sql(self, expression: exp.AnalyzeDelete) -> str:
5022        kind = self.sql(expression, "kind")
5023        kind = f" {kind}" if kind else ""
5024        return f"DELETE{kind} STATISTICS"
5025
5026    def analyzelistchainedrows_sql(self, expression: exp.AnalyzeListChainedRows) -> str:
5027        inner_expression = self.sql(expression, "expression")
5028        return f"LIST CHAINED ROWS{inner_expression}"
5029
5030    def analyzevalidate_sql(self, expression: exp.AnalyzeValidate) -> str:
5031        kind = self.sql(expression, "kind")
5032        this = self.sql(expression, "this")
5033        this = f" {this}" if this else ""
5034        inner_expression = self.sql(expression, "expression")
5035        return f"VALIDATE {kind}{this}{inner_expression}"
5036
5037    def analyze_sql(self, expression: exp.Analyze) -> str:
5038        options = self.expressions(expression, key="options", sep=" ")
5039        options = f" {options}" if options else ""
5040        kind = self.sql(expression, "kind")
5041        kind = f" {kind}" if kind else ""
5042        this = self.sql(expression, "this")
5043        this = f" {this}" if this else ""
5044        mode = self.sql(expression, "mode")
5045        mode = f" {mode}" if mode else ""
5046        properties = self.sql(expression, "properties")
5047        properties = f" {properties}" if properties else ""
5048        partition = self.sql(expression, "partition")
5049        partition = f" {partition}" if partition else ""
5050        inner_expression = self.sql(expression, "expression")
5051        inner_expression = f" {inner_expression}" if inner_expression else ""
5052        return f"ANALYZE{options}{kind}{this}{partition}{mode}{inner_expression}{properties}"
5053
5054    def xmltable_sql(self, expression: exp.XMLTable) -> str:
5055        this = self.sql(expression, "this")
5056        namespaces = self.expressions(expression, key="namespaces")
5057        namespaces = f"XMLNAMESPACES({namespaces}), " if namespaces else ""
5058        passing = self.expressions(expression, key="passing")
5059        passing = f"{self.sep()}PASSING{self.seg(passing)}" if passing else ""
5060        columns = self.expressions(expression, key="columns")
5061        columns = f"{self.sep()}COLUMNS{self.seg(columns)}" if columns else ""
5062        by_ref = f"{self.sep()}RETURNING SEQUENCE BY REF" if expression.args.get("by_ref") else ""
5063        return f"XMLTABLE({self.sep('')}{self.indent(namespaces + this + passing + by_ref + columns)}{self.seg(')', sep='')}"
5064
5065    def xmlnamespace_sql(self, expression: exp.XMLNamespace) -> str:
5066        this = self.sql(expression, "this")
5067        return this if isinstance(expression.this, exp.Alias) else f"DEFAULT {this}"
5068
5069    def export_sql(self, expression: exp.Export) -> str:
5070        this = self.sql(expression, "this")
5071        connection = self.sql(expression, "connection")
5072        connection = f"WITH CONNECTION {connection} " if connection else ""
5073        options = self.sql(expression, "options")
5074        return f"EXPORT DATA {connection}{options} AS {this}"
5075
5076    def declare_sql(self, expression: exp.Declare) -> str:
5077        return f"DECLARE {self.expressions(expression, flat=True)}"
5078
5079    def declareitem_sql(self, expression: exp.DeclareItem) -> str:
5080        variable = self.sql(expression, "this")
5081        default = self.sql(expression, "default")
5082        default = f" = {default}" if default else ""
5083
5084        kind = self.sql(expression, "kind")
5085        if isinstance(expression.args.get("kind"), exp.Schema):
5086            kind = f"TABLE {kind}"
5087
5088        return f"{variable} AS {kind}{default}"
5089
5090    def recursivewithsearch_sql(self, expression: exp.RecursiveWithSearch) -> str:
5091        kind = self.sql(expression, "kind")
5092        this = self.sql(expression, "this")
5093        set = self.sql(expression, "expression")
5094        using = self.sql(expression, "using")
5095        using = f" USING {using}" if using else ""
5096
5097        kind_sql = kind if kind == "CYCLE" else f"SEARCH {kind} FIRST BY"
5098
5099        return f"{kind_sql} {this} SET {set}{using}"
5100
5101    def parameterizedagg_sql(self, expression: exp.ParameterizedAgg) -> str:
5102        params = self.expressions(expression, key="params", flat=True)
5103        return self.func(expression.name, *expression.expressions) + f"({params})"
5104
5105    def anonymousaggfunc_sql(self, expression: exp.AnonymousAggFunc) -> str:
5106        return self.func(expression.name, *expression.expressions)
5107
5108    def combinedaggfunc_sql(self, expression: exp.CombinedAggFunc) -> str:
5109        return self.anonymousaggfunc_sql(expression)
5110
5111    def combinedparameterizedagg_sql(self, expression: exp.CombinedParameterizedAgg) -> str:
5112        return self.parameterizedagg_sql(expression)
5113
5114    def show_sql(self, expression: exp.Show) -> str:
5115        self.unsupported("Unsupported SHOW statement")
5116        return ""
5117
5118    def get_put_sql(self, expression: exp.Put | exp.Get) -> str:
5119        # Snowflake GET/PUT statements:
5120        #   PUT <file> <internalStage> <properties>
5121        #   GET <internalStage> <file> <properties>
5122        props = expression.args.get("properties")
5123        props_sql = self.properties(props, prefix=" ", sep=" ", wrapped=False) if props else ""
5124        this = self.sql(expression, "this")
5125        target = self.sql(expression, "target")
5126
5127        if isinstance(expression, exp.Put):
5128            return f"PUT {this} {target}{props_sql}"
5129        else:
5130            return f"GET {target} {this}{props_sql}"
5131
5132    def translatecharacters_sql(self, expression: exp.TranslateCharacters):
5133        this = self.sql(expression, "this")
5134        expr = self.sql(expression, "expression")
5135        with_error = " WITH ERROR" if expression.args.get("with_error") else ""
5136        return f"TRANSLATE({this} USING {expr}{with_error})"
5137
5138    def decodecase_sql(self, expression: exp.DecodeCase) -> str:
5139        if self.SUPPORTS_DECODE_CASE:
5140            return self.func("DECODE", *expression.expressions)
5141
5142        expression, *expressions = expression.expressions
5143
5144        ifs = []
5145        for search, result in zip(expressions[::2], expressions[1::2]):
5146            if isinstance(search, exp.Literal):
5147                ifs.append(exp.If(this=expression.eq(search), true=result))
5148            elif isinstance(search, exp.Null):
5149                ifs.append(exp.If(this=expression.is_(exp.Null()), true=result))
5150            else:
5151                if isinstance(search, exp.Binary):
5152                    search = exp.paren(search)
5153
5154                cond = exp.or_(
5155                    expression.eq(search),
5156                    exp.and_(expression.is_(exp.Null()), search.is_(exp.Null()), copy=False),
5157                    copy=False,
5158                )
5159                ifs.append(exp.If(this=cond, true=result))
5160
5161        case = exp.Case(ifs=ifs, default=expressions[-1] if len(expressions) % 2 == 1 else None)
5162        return self.sql(case)
5163
5164    def semanticview_sql(self, expression: exp.SemanticView) -> str:
5165        this = self.sql(expression, "this")
5166        this = self.seg(this, sep="")
5167        dimensions = self.expressions(
5168            expression, "dimensions", dynamic=True, skip_first=True, skip_last=True
5169        )
5170        dimensions = self.seg(f"DIMENSIONS {dimensions}") if dimensions else ""
5171        metrics = self.expressions(
5172            expression, "metrics", dynamic=True, skip_first=True, skip_last=True
5173        )
5174        metrics = self.seg(f"METRICS {metrics}") if metrics else ""
5175        where = self.sql(expression, "where")
5176        where = self.seg(f"WHERE {where}") if where else ""
5177        return f"SEMANTIC_VIEW({self.indent(this + metrics + dimensions + where)}{self.seg(')', sep='')}"
5178
5179    def getextract_sql(self, expression: exp.GetExtract) -> str:
5180        this = expression.this
5181        expr = expression.expression
5182
5183        if not this.type or not expression.type:
5184            from sqlglot.optimizer.annotate_types import annotate_types
5185
5186            this = annotate_types(this, dialect=self.dialect)
5187
5188        if this.is_type(*(exp.DataType.Type.ARRAY, exp.DataType.Type.MAP)):
5189            return self.sql(exp.Bracket(this=this, expressions=[expr]))
5190
5191        return self.sql(exp.JSONExtract(this=this, expression=self.dialect.to_json_path(expr)))
5192
5193    def datefromunixdate_sql(self, expression: exp.DateFromUnixDate) -> str:
5194        return self.sql(
5195            exp.DateAdd(
5196                this=exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
5197                expression=expression.this,
5198                unit=exp.var("DAY"),
5199            )
5200        )
5201
5202    def space_sql(self: Generator, expression: exp.Space) -> str:
5203        return self.sql(exp.Repeat(this=exp.Literal.string(" "), times=expression.this))
5204
5205    def buildproperty_sql(self, expression: exp.BuildProperty) -> str:
5206        return f"BUILD {self.sql(expression, 'this')}"
5207
5208    def refreshtriggerproperty_sql(self, expression: exp.RefreshTriggerProperty) -> str:
5209        method = self.sql(expression, "method")
5210        kind = expression.args.get("kind")
5211        if not kind:
5212            return f"REFRESH {method}"
5213
5214        every = self.sql(expression, "every")
5215        unit = self.sql(expression, "unit")
5216        every = f" EVERY {every} {unit}" if every else ""
5217        starts = self.sql(expression, "starts")
5218        starts = f" STARTS {starts}" if starts else ""
5219
5220        return f"REFRESH {method} ON {kind}{every}{starts}"

Generator converts a given syntax tree to the corresponding SQL string.

Arguments:
  • pretty: Whether to format the produced SQL string. Default: False.
  • identify: Determines when an identifier should be quoted. Possible values are: False (default): Never quote, except in cases where it's mandatory by the dialect. True or 'always': Always quote. 'safe': Only quote identifiers that are case insensitive.
  • normalize: Whether to normalize identifiers to lowercase. Default: False.
  • pad: The pad size in a formatted string. For example, this affects the indentation of a projection in a query, relative to its nesting level. Default: 2.
  • indent: The indentation size in a formatted string. For example, this affects the indentation of subqueries and filters under a WHERE clause. Default: 2.
  • normalize_functions: How to normalize function names. Possible values are: "upper" or True (default): Convert names to uppercase. "lower": Convert names to lowercase. False: Disables function name normalization.
  • unsupported_level: Determines the generator's behavior when it encounters unsupported expressions. Default ErrorLevel.WARN.
  • max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError. This is only relevant if unsupported_level is ErrorLevel.RAISE. Default: 3
  • leading_comma: Whether the comma is leading or trailing in select expressions. This is only relevant when generating in pretty mode. Default: False
  • max_text_width: The max number of characters in a segment before creating new lines in pretty mode. The default is on the smaller end because the length only represents a segment and not the true line length. Default: 80
  • comments: Whether to preserve comments in the output SQL code. Default: True
Generator( pretty: Optional[bool] = None, identify: str | bool = False, normalize: bool = False, pad: int = 2, indent: int = 2, normalize_functions: Union[str, bool, NoneType] = None, unsupported_level: sqlglot.errors.ErrorLevel = <ErrorLevel.WARN: 'WARN'>, max_unsupported: int = 3, leading_comma: bool = False, max_text_width: int = 80, comments: bool = True, dialect: Union[str, sqlglot.dialects.Dialect, Type[sqlglot.dialects.Dialect], NoneType] = None)
722    def __init__(
723        self,
724        pretty: t.Optional[bool] = None,
725        identify: str | bool = False,
726        normalize: bool = False,
727        pad: int = 2,
728        indent: int = 2,
729        normalize_functions: t.Optional[str | bool] = None,
730        unsupported_level: ErrorLevel = ErrorLevel.WARN,
731        max_unsupported: int = 3,
732        leading_comma: bool = False,
733        max_text_width: int = 80,
734        comments: bool = True,
735        dialect: DialectType = None,
736    ):
737        import sqlglot
738        from sqlglot.dialects import Dialect
739
740        self.pretty = pretty if pretty is not None else sqlglot.pretty
741        self.identify = identify
742        self.normalize = normalize
743        self.pad = pad
744        self._indent = indent
745        self.unsupported_level = unsupported_level
746        self.max_unsupported = max_unsupported
747        self.leading_comma = leading_comma
748        self.max_text_width = max_text_width
749        self.comments = comments
750        self.dialect = Dialect.get_or_raise(dialect)
751
752        # This is both a Dialect property and a Generator argument, so we prioritize the latter
753        self.normalize_functions = (
754            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
755        )
756
757        self.unsupported_messages: t.List[str] = []
758        self._escaped_quote_end: str = (
759            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
760        )
761        self._escaped_identifier_end = self.dialect.IDENTIFIER_END * 2
762
763        self._next_name = name_sequence("_t")
764
765        self._identifier_start = self.dialect.IDENTIFIER_START
766        self._identifier_end = self.dialect.IDENTIFIER_END
767
768        self._quote_json_path_key_using_brackets = True
TRANSFORMS: Dict[Type[sqlglot.expressions.Expression], Callable[..., str]] = {<class 'sqlglot.expressions.JSONPathFilter'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathKey'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRecursive'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRoot'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathScript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSelector'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSlice'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSubscript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathUnion'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathWildcard'>: <function <lambda>>, <class 'sqlglot.expressions.AllowedValuesProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.AnalyzeColumns'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.AnalyzeWith'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ArrayContainsAll'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ArrayOverlaps'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.BackupProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CaseSpecificColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Ceil'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CollateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CommentColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ConnectByRoot'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ConvertToCharset'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CredentialsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DateFormatColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DefaultColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DynamicProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EmptyProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EncodeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EnviromentProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EphemeralColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExcludeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Except'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExternalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Floor'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Get'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.GlobalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.HeapProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IcebergProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InheritsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InlineLengthColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Intersect'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IntervalSpan'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Int64'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LanguageProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LocationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LogProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.MaterializedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NonClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NotForReplicationColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnCommitProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnUpdateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Operator'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OutputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PathColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PartitionedByBucket'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PartitionByTruncate'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PivotAny'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PositionalColumn'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ProjectionPolicyColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Put'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ReturnsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SampleProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SecureProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetConfigProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SettingsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SharingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StabilityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Stream'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StreamingTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StrictProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SwapTable'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TableColumn'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Tags'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TemporaryProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TitleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransformModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransientProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Union'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UnloggedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UsingTemplateProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UsingData'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Uuid'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UppercaseColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VarMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WeekStart'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithProcedureOptions'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithSchemaBindingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithOperator'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ForceProperty'>: <function Generator.<lambda>>}
NULL_ORDERING_SUPPORTED: Optional[bool] = True
IGNORE_NULLS_IN_FUNC = False
LOCKING_READS_SUPPORTED = False
EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = True
WRAP_DERIVED_VALUES = True
CREATE_FUNCTION_RETURN_AS = True
MATCHED_BY_SOURCE = True
SINGLE_STRING_INTERVAL = False
INTERVAL_ALLOWS_PLURAL_FORM = True
LIMIT_FETCH = 'ALL'
LIMIT_ONLY_LITERALS = False
RENAME_TABLE_WITH_DB = True
GROUPINGS_SEP = ','
INDEX_ON = 'ON'
JOIN_HINTS = True
TABLE_HINTS = True
QUERY_HINTS = True
QUERY_HINT_SEP = ', '
IS_BOOL_ALLOWED = True
DUPLICATE_KEY_UPDATE_WITH_SET = True
LIMIT_IS_TOP = False
RETURNING_END = True
EXTRACT_ALLOWS_QUOTES = True
TZ_TO_WITH_TIME_ZONE = False
NVL2_SUPPORTED = True
SELECT_KINDS: Tuple[str, ...] = ('STRUCT', 'VALUE')
VALUES_AS_TABLE = True
ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
UNNEST_WITH_ORDINALITY = True
AGGREGATE_FILTER_SUPPORTED = True
SEMI_ANTI_JOIN_WITH_SIDE = True
COMPUTED_COLUMN_WITH_TYPE = True
SUPPORTS_TABLE_COPY = True
TABLESAMPLE_REQUIRES_PARENS = True
TABLESAMPLE_SIZE_IS_ROWS = True
TABLESAMPLE_KEYWORDS = 'TABLESAMPLE'
TABLESAMPLE_WITH_METHOD = True
TABLESAMPLE_SEED_KEYWORD = 'SEED'
COLLATE_IS_FUNC = False
DATA_TYPE_SPECIFIERS_ALLOWED = False
ENSURE_BOOLS = False
CTE_RECURSIVE_KEYWORD_REQUIRED = True
SUPPORTS_SINGLE_ARG_CONCAT = True
LAST_DAY_SUPPORTS_DATE_PART = True
SUPPORTS_TABLE_ALIAS_COLUMNS = True
UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
JSON_KEY_VALUE_PAIR_SEP = ':'
INSERT_OVERWRITE = ' OVERWRITE TABLE'
SUPPORTS_SELECT_INTO = False
SUPPORTS_UNLOGGED_TABLES = False
SUPPORTS_CREATE_TABLE_LIKE = True
LIKE_PROPERTY_INSIDE_SCHEMA = False
MULTI_ARG_DISTINCT = True
JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
JSON_PATH_BRACKETED_KEY_SUPPORTED = True
JSON_PATH_SINGLE_QUOTE_ESCAPE = False
CAN_IMPLEMENT_ARRAY_ANY = False
SUPPORTS_TO_NUMBER = True
SUPPORTS_WINDOW_EXCLUDE = False
SET_OP_MODIFIERS = True
COPY_PARAMS_ARE_WRAPPED = True
COPY_PARAMS_EQ_REQUIRED = False
COPY_HAS_INTO_KEYWORD = True
TRY_SUPPORTED = True
SUPPORTS_UESCAPE = True
STAR_EXCEPT = 'EXCEPT'
HEX_FUNC = 'HEX'
WITH_PROPERTIES_PREFIX = 'WITH'
QUOTE_JSON_PATH = True
PAD_FILL_PATTERN_IS_REQUIRED = False
SUPPORTS_EXPLODING_PROJECTIONS = True
ARRAY_CONCAT_IS_VAR_LEN = True
SUPPORTS_CONVERT_TIMEZONE = False
SUPPORTS_MEDIAN = True
SUPPORTS_UNIX_SECONDS = False
ALTER_SET_WRAPPED = False
NORMALIZE_EXTRACT_DATE_PARTS = False
PARSE_JSON_NAME: Optional[str] = 'PARSE_JSON'
ARRAY_SIZE_NAME: str = 'ARRAY_LENGTH'
ALTER_SET_TYPE = 'SET DATA TYPE'
ARRAY_SIZE_DIM_REQUIRED: Optional[bool] = None
SUPPORTS_DECODE_CASE = True
SUPPORTS_BETWEEN_FLAGS = False
SUPPORTS_LIKE_QUANTIFIERS = True
TYPE_MAPPING = {<Type.DATETIME2: 'DATETIME2'>: 'TIMESTAMP', <Type.NCHAR: 'NCHAR'>: 'CHAR', <Type.NVARCHAR: 'NVARCHAR'>: 'VARCHAR', <Type.MEDIUMTEXT: 'MEDIUMTEXT'>: 'TEXT', <Type.LONGTEXT: 'LONGTEXT'>: 'TEXT', <Type.TINYTEXT: 'TINYTEXT'>: 'TEXT', <Type.BLOB: 'BLOB'>: 'VARBINARY', <Type.MEDIUMBLOB: 'MEDIUMBLOB'>: 'BLOB', <Type.LONGBLOB: 'LONGBLOB'>: 'BLOB', <Type.TINYBLOB: 'TINYBLOB'>: 'BLOB', <Type.INET: 'INET'>: 'INET', <Type.ROWVERSION: 'ROWVERSION'>: 'VARBINARY', <Type.SMALLDATETIME: 'SMALLDATETIME'>: 'TIMESTAMP'}
TIME_PART_SINGULARS = {'MICROSECONDS': 'MICROSECOND', 'SECONDS': 'SECOND', 'MINUTES': 'MINUTE', 'HOURS': 'HOUR', 'DAYS': 'DAY', 'WEEKS': 'WEEK', 'MONTHS': 'MONTH', 'QUARTERS': 'QUARTER', 'YEARS': 'YEAR'}
AFTER_HAVING_MODIFIER_TRANSFORMS = {'cluster': <function Generator.<lambda>>, 'distribute': <function Generator.<lambda>>, 'sort': <function Generator.<lambda>>, 'windows': <function Generator.<lambda>>, 'qualify': <function Generator.<lambda>>}
TOKEN_MAPPING: Dict[sqlglot.tokens.TokenType, str] = {}
STRUCT_DELIMITER = ('<', '>')
PARAMETER_TOKEN = '@'
NAMED_PLACEHOLDER_TOKEN = ':'
EXPRESSION_PRECEDES_PROPERTIES_CREATABLES: Set[str] = set()
PROPERTIES_LOCATION = {<class 'sqlglot.expressions.AllowedValuesProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AlgorithmProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.AutoIncrementProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BackupProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BlockCompressionProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CharacterSetProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ChecksumProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CollateProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Cluster'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ClusteredByProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistributedByProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DuplicateKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DataBlocksizeProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.DataDeletionProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DefinerProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.DictRange'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DictProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DynamicProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.DistKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistStyleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.EmptyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.EncodeProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.EngineProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.EnviromentProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExternalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.FallbackProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.FileFormatProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.FreespaceProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.GlobalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.HeapProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.InheritsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IcebergProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.IncludeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.InputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IsolatedLoadingProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.JournalProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.LanguageProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LikeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LocationProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockingProperty'>: <Location.POST_ALIAS: 'POST_ALIAS'>, <class 'sqlglot.expressions.LogProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.MaterializedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.MergeBlockRatioProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.OnProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OnCommitProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.Order'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OutputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PartitionedByProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.PartitionedOfProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PrimaryKey'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Property'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ReturnsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatDelimitedProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatSerdeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SampleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SchemaCommentProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SecureProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SecurityProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SerdeProperties'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Set'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SettingsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SetProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SetConfigProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SharingProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SequenceProperties'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SortKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.StabilityProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.StorageHandlerProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.StreamingTableProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.StrictProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Tags'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.TemporaryProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ToTableProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TransientProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.TransformModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.MergeTreeTTL'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.UnloggedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.UsingTemplateProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.VolatileProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.WithDataProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.WithProcedureOptions'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.WithSchemaBindingProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.WithSystemVersioningProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ForceProperty'>: <Location.POST_CREATE: 'POST_CREATE'>}
RESERVED_KEYWORDS: Set[str] = set()
EXCLUDE_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Binary'>, <class 'sqlglot.expressions.SetOperation'>)
UNWRAPPED_INTERVAL_VALUES: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Column'>, <class 'sqlglot.expressions.Literal'>, <class 'sqlglot.expressions.Neg'>, <class 'sqlglot.expressions.Paren'>)
PARAMETERIZABLE_TEXT_TYPES = {<Type.CHAR: 'CHAR'>, <Type.NVARCHAR: 'NVARCHAR'>, <Type.NCHAR: 'NCHAR'>, <Type.VARCHAR: 'VARCHAR'>}
EXPRESSIONS_WITHOUT_NESTED_CTES: Set[Type[sqlglot.expressions.Expression]] = set()
RESPECT_IGNORE_NULLS_UNSUPPORTED_EXPRESSIONS: Tuple[Type[sqlglot.expressions.Expression], ...] = ()
SAFE_JSON_PATH_KEY_RE = re.compile('^[_a-zA-Z][\\w]*$')
SENTINEL_LINE_BREAK = '__SQLGLOT__LB__'
pretty
identify
normalize
pad
unsupported_level
max_unsupported
leading_comma
max_text_width
comments
dialect
normalize_functions
unsupported_messages: List[str]
def generate( self, expression: sqlglot.expressions.Expression, copy: bool = True) -> str:
770    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
771        """
772        Generates the SQL string corresponding to the given syntax tree.
773
774        Args:
775            expression: The syntax tree.
776            copy: Whether to copy the expression. The generator performs mutations so
777                it is safer to copy.
778
779        Returns:
780            The SQL string corresponding to `expression`.
781        """
782        if copy:
783            expression = expression.copy()
784
785        expression = self.preprocess(expression)
786
787        self.unsupported_messages = []
788        sql = self.sql(expression).strip()
789
790        if self.pretty:
791            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
792
793        if self.unsupported_level == ErrorLevel.IGNORE:
794            return sql
795
796        if self.unsupported_level == ErrorLevel.WARN:
797            for msg in self.unsupported_messages:
798                logger.warning(msg)
799        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
800            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
801
802        return sql

Generates the SQL string corresponding to the given syntax tree.

Arguments:
  • expression: The syntax tree.
  • copy: Whether to copy the expression. The generator performs mutations so it is safer to copy.
Returns:

The SQL string corresponding to expression.

def preprocess( self, expression: sqlglot.expressions.Expression) -> sqlglot.expressions.Expression:
804    def preprocess(self, expression: exp.Expression) -> exp.Expression:
805        """Apply generic preprocessing transformations to a given expression."""
806        expression = self._move_ctes_to_top_level(expression)
807
808        if self.ENSURE_BOOLS:
809            from sqlglot.transforms import ensure_bools
810
811            expression = ensure_bools(expression)
812
813        return expression

Apply generic preprocessing transformations to a given expression.

def unsupported(self, message: str) -> None:
826    def unsupported(self, message: str) -> None:
827        if self.unsupported_level == ErrorLevel.IMMEDIATE:
828            raise UnsupportedError(message)
829        self.unsupported_messages.append(message)
def sep(self, sep: str = ' ') -> str:
831    def sep(self, sep: str = " ") -> str:
832        return f"{sep.strip()}\n" if self.pretty else sep
def seg(self, sql: str, sep: str = ' ') -> str:
834    def seg(self, sql: str, sep: str = " ") -> str:
835        return f"{self.sep(sep)}{sql}"
def sanitize_comment(self, comment: str) -> str:
837    def sanitize_comment(self, comment: str) -> str:
838        comment = " " + comment if comment[0].strip() else comment
839        comment = comment + " " if comment[-1].strip() else comment
840
841        if not self.dialect.tokenizer_class.NESTED_COMMENTS:
842            # Necessary workaround to avoid syntax errors due to nesting: /* ... */ ... */
843            comment = comment.replace("*/", "* /")
844
845        return comment
def maybe_comment( self, sql: str, expression: Optional[sqlglot.expressions.Expression] = None, comments: Optional[List[str]] = None, separated: bool = False) -> str:
847    def maybe_comment(
848        self,
849        sql: str,
850        expression: t.Optional[exp.Expression] = None,
851        comments: t.Optional[t.List[str]] = None,
852        separated: bool = False,
853    ) -> str:
854        comments = (
855            ((expression and expression.comments) if comments is None else comments)  # type: ignore
856            if self.comments
857            else None
858        )
859
860        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
861            return sql
862
863        comments_sql = " ".join(
864            f"/*{self.sanitize_comment(comment)}*/" for comment in comments if comment
865        )
866
867        if not comments_sql:
868            return sql
869
870        comments_sql = self._replace_line_breaks(comments_sql)
871
872        if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS):
873            return (
874                f"{self.sep()}{comments_sql}{sql}"
875                if not sql or sql[0].isspace()
876                else f"{comments_sql}{self.sep()}{sql}"
877            )
878
879        return f"{sql} {comments_sql}"
def wrap(self, expression: sqlglot.expressions.Expression | str) -> str:
881    def wrap(self, expression: exp.Expression | str) -> str:
882        this_sql = (
883            self.sql(expression)
884            if isinstance(expression, exp.UNWRAPPED_QUERIES)
885            else self.sql(expression, "this")
886        )
887        if not this_sql:
888            return "()"
889
890        this_sql = self.indent(this_sql, level=1, pad=0)
891        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
def no_identify(self, func: Callable[..., str], *args, **kwargs) -> str:
893    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
894        original = self.identify
895        self.identify = False
896        result = func(*args, **kwargs)
897        self.identify = original
898        return result
def normalize_func(self, name: str) -> str:
900    def normalize_func(self, name: str) -> str:
901        if self.normalize_functions == "upper" or self.normalize_functions is True:
902            return name.upper()
903        if self.normalize_functions == "lower":
904            return name.lower()
905        return name
def indent( self, sql: str, level: int = 0, pad: Optional[int] = None, skip_first: bool = False, skip_last: bool = False) -> str:
907    def indent(
908        self,
909        sql: str,
910        level: int = 0,
911        pad: t.Optional[int] = None,
912        skip_first: bool = False,
913        skip_last: bool = False,
914    ) -> str:
915        if not self.pretty or not sql:
916            return sql
917
918        pad = self.pad if pad is None else pad
919        lines = sql.split("\n")
920
921        return "\n".join(
922            (
923                line
924                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
925                else f"{' ' * (level * self._indent + pad)}{line}"
926            )
927            for i, line in enumerate(lines)
928        )
def sql( self, expression: Union[str, sqlglot.expressions.Expression, NoneType], key: Optional[str] = None, comment: bool = True) -> str:
930    def sql(
931        self,
932        expression: t.Optional[str | exp.Expression],
933        key: t.Optional[str] = None,
934        comment: bool = True,
935    ) -> str:
936        if not expression:
937            return ""
938
939        if isinstance(expression, str):
940            return expression
941
942        if key:
943            value = expression.args.get(key)
944            if value:
945                return self.sql(value)
946            return ""
947
948        transform = self.TRANSFORMS.get(expression.__class__)
949
950        if callable(transform):
951            sql = transform(self, expression)
952        elif isinstance(expression, exp.Expression):
953            exp_handler_name = f"{expression.key}_sql"
954
955            if hasattr(self, exp_handler_name):
956                sql = getattr(self, exp_handler_name)(expression)
957            elif isinstance(expression, exp.Func):
958                sql = self.function_fallback_sql(expression)
959            elif isinstance(expression, exp.Property):
960                sql = self.property_sql(expression)
961            else:
962                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
963        else:
964            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
965
966        return self.maybe_comment(sql, expression) if self.comments and comment else sql
def uncache_sql(self, expression: sqlglot.expressions.Uncache) -> str:
968    def uncache_sql(self, expression: exp.Uncache) -> str:
969        table = self.sql(expression, "this")
970        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
971        return f"UNCACHE TABLE{exists_sql} {table}"
def cache_sql(self, expression: sqlglot.expressions.Cache) -> str:
973    def cache_sql(self, expression: exp.Cache) -> str:
974        lazy = " LAZY" if expression.args.get("lazy") else ""
975        table = self.sql(expression, "this")
976        options = expression.args.get("options")
977        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
978        sql = self.sql(expression, "expression")
979        sql = f" AS{self.sep()}{sql}" if sql else ""
980        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
981        return self.prepend_ctes(expression, sql)
def characterset_sql(self, expression: sqlglot.expressions.CharacterSet) -> str:
983    def characterset_sql(self, expression: exp.CharacterSet) -> str:
984        if isinstance(expression.parent, exp.Cast):
985            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
986        default = "DEFAULT " if expression.args.get("default") else ""
987        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
def column_parts(self, expression: sqlglot.expressions.Column) -> str:
989    def column_parts(self, expression: exp.Column) -> str:
990        return ".".join(
991            self.sql(part)
992            for part in (
993                expression.args.get("catalog"),
994                expression.args.get("db"),
995                expression.args.get("table"),
996                expression.args.get("this"),
997            )
998            if part
999        )
def column_sql(self, expression: sqlglot.expressions.Column) -> str:
1001    def column_sql(self, expression: exp.Column) -> str:
1002        join_mark = " (+)" if expression.args.get("join_mark") else ""
1003
1004        if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS:
1005            join_mark = ""
1006            self.unsupported("Outer join syntax using the (+) operator is not supported.")
1007
1008        return f"{self.column_parts(expression)}{join_mark}"
def columnposition_sql(self, expression: sqlglot.expressions.ColumnPosition) -> str:
1010    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
1011        this = self.sql(expression, "this")
1012        this = f" {this}" if this else ""
1013        position = self.sql(expression, "position")
1014        return f"{position}{this}"
def columndef_sql(self, expression: sqlglot.expressions.ColumnDef, sep: str = ' ') -> str:
1016    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
1017        column = self.sql(expression, "this")
1018        kind = self.sql(expression, "kind")
1019        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
1020        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
1021        kind = f"{sep}{kind}" if kind else ""
1022        constraints = f" {constraints}" if constraints else ""
1023        position = self.sql(expression, "position")
1024        position = f" {position}" if position else ""
1025
1026        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
1027            kind = ""
1028
1029        return f"{exists}{column}{kind}{constraints}{position}"
def columnconstraint_sql(self, expression: sqlglot.expressions.ColumnConstraint) -> str:
1031    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
1032        this = self.sql(expression, "this")
1033        kind_sql = self.sql(expression, "kind").strip()
1034        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
def computedcolumnconstraint_sql(self, expression: sqlglot.expressions.ComputedColumnConstraint) -> str:
1036    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
1037        this = self.sql(expression, "this")
1038        if expression.args.get("not_null"):
1039            persisted = " PERSISTED NOT NULL"
1040        elif expression.args.get("persisted"):
1041            persisted = " PERSISTED"
1042        else:
1043            persisted = ""
1044
1045        return f"AS {this}{persisted}"
def autoincrementcolumnconstraint_sql(self, _) -> str:
1047    def autoincrementcolumnconstraint_sql(self, _) -> str:
1048        return self.token_sql(TokenType.AUTO_INCREMENT)
def compresscolumnconstraint_sql(self, expression: sqlglot.expressions.CompressColumnConstraint) -> str:
1050    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
1051        if isinstance(expression.this, list):
1052            this = self.wrap(self.expressions(expression, key="this", flat=True))
1053        else:
1054            this = self.sql(expression, "this")
1055
1056        return f"COMPRESS {this}"
def generatedasidentitycolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str:
1058    def generatedasidentitycolumnconstraint_sql(
1059        self, expression: exp.GeneratedAsIdentityColumnConstraint
1060    ) -> str:
1061        this = ""
1062        if expression.this is not None:
1063            on_null = " ON NULL" if expression.args.get("on_null") else ""
1064            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
1065
1066        start = expression.args.get("start")
1067        start = f"START WITH {start}" if start else ""
1068        increment = expression.args.get("increment")
1069        increment = f" INCREMENT BY {increment}" if increment else ""
1070        minvalue = expression.args.get("minvalue")
1071        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
1072        maxvalue = expression.args.get("maxvalue")
1073        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
1074        cycle = expression.args.get("cycle")
1075        cycle_sql = ""
1076
1077        if cycle is not None:
1078            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
1079            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
1080
1081        sequence_opts = ""
1082        if start or increment or cycle_sql:
1083            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
1084            sequence_opts = f" ({sequence_opts.strip()})"
1085
1086        expr = self.sql(expression, "expression")
1087        expr = f"({expr})" if expr else "IDENTITY"
1088
1089        return f"GENERATED{this} AS {expr}{sequence_opts}"
def generatedasrowcolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsRowColumnConstraint) -> str:
1091    def generatedasrowcolumnconstraint_sql(
1092        self, expression: exp.GeneratedAsRowColumnConstraint
1093    ) -> str:
1094        start = "START" if expression.args.get("start") else "END"
1095        hidden = " HIDDEN" if expression.args.get("hidden") else ""
1096        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
def periodforsystemtimeconstraint_sql( self, expression: sqlglot.expressions.PeriodForSystemTimeConstraint) -> str:
1098    def periodforsystemtimeconstraint_sql(
1099        self, expression: exp.PeriodForSystemTimeConstraint
1100    ) -> str:
1101        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
def notnullcolumnconstraint_sql(self, expression: sqlglot.expressions.NotNullColumnConstraint) -> str:
1103    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
1104        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
def primarykeycolumnconstraint_sql(self, expression: sqlglot.expressions.PrimaryKeyColumnConstraint) -> str:
1106    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
1107        desc = expression.args.get("desc")
1108        if desc is not None:
1109            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
1110        options = self.expressions(expression, key="options", flat=True, sep=" ")
1111        options = f" {options}" if options else ""
1112        return f"PRIMARY KEY{options}"
def uniquecolumnconstraint_sql(self, expression: sqlglot.expressions.UniqueColumnConstraint) -> str:
1114    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
1115        this = self.sql(expression, "this")
1116        this = f" {this}" if this else ""
1117        index_type = expression.args.get("index_type")
1118        index_type = f" USING {index_type}" if index_type else ""
1119        on_conflict = self.sql(expression, "on_conflict")
1120        on_conflict = f" {on_conflict}" if on_conflict else ""
1121        nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else ""
1122        options = self.expressions(expression, key="options", flat=True, sep=" ")
1123        options = f" {options}" if options else ""
1124        return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}{options}"
def createable_sql( self, expression: sqlglot.expressions.Create, locations: DefaultDict) -> str:
1126    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
1127        return self.sql(expression, "this")
def create_sql(self, expression: sqlglot.expressions.Create) -> str:
1129    def create_sql(self, expression: exp.Create) -> str:
1130        kind = self.sql(expression, "kind")
1131        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
1132        properties = expression.args.get("properties")
1133        properties_locs = self.locate_properties(properties) if properties else defaultdict()
1134
1135        this = self.createable_sql(expression, properties_locs)
1136
1137        properties_sql = ""
1138        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
1139            exp.Properties.Location.POST_WITH
1140        ):
1141            props_ast = exp.Properties(
1142                expressions=[
1143                    *properties_locs[exp.Properties.Location.POST_SCHEMA],
1144                    *properties_locs[exp.Properties.Location.POST_WITH],
1145                ]
1146            )
1147            props_ast.parent = expression
1148            properties_sql = self.sql(props_ast)
1149
1150            if properties_locs.get(exp.Properties.Location.POST_SCHEMA):
1151                properties_sql = self.sep() + properties_sql
1152            elif not self.pretty:
1153                # Standalone POST_WITH properties need a leading whitespace in non-pretty mode
1154                properties_sql = f" {properties_sql}"
1155
1156        begin = " BEGIN" if expression.args.get("begin") else ""
1157        end = " END" if expression.args.get("end") else ""
1158
1159        expression_sql = self.sql(expression, "expression")
1160        if expression_sql:
1161            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
1162
1163            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
1164                postalias_props_sql = ""
1165                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
1166                    postalias_props_sql = self.properties(
1167                        exp.Properties(
1168                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
1169                        ),
1170                        wrapped=False,
1171                    )
1172                postalias_props_sql = f" {postalias_props_sql}" if postalias_props_sql else ""
1173                expression_sql = f" AS{postalias_props_sql}{expression_sql}"
1174
1175        postindex_props_sql = ""
1176        if properties_locs.get(exp.Properties.Location.POST_INDEX):
1177            postindex_props_sql = self.properties(
1178                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
1179                wrapped=False,
1180                prefix=" ",
1181            )
1182
1183        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
1184        indexes = f" {indexes}" if indexes else ""
1185        index_sql = indexes + postindex_props_sql
1186
1187        replace = " OR REPLACE" if expression.args.get("replace") else ""
1188        refresh = " OR REFRESH" if expression.args.get("refresh") else ""
1189        unique = " UNIQUE" if expression.args.get("unique") else ""
1190
1191        clustered = expression.args.get("clustered")
1192        if clustered is None:
1193            clustered_sql = ""
1194        elif clustered:
1195            clustered_sql = " CLUSTERED COLUMNSTORE"
1196        else:
1197            clustered_sql = " NONCLUSTERED COLUMNSTORE"
1198
1199        postcreate_props_sql = ""
1200        if properties_locs.get(exp.Properties.Location.POST_CREATE):
1201            postcreate_props_sql = self.properties(
1202                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
1203                sep=" ",
1204                prefix=" ",
1205                wrapped=False,
1206            )
1207
1208        modifiers = "".join((clustered_sql, replace, refresh, unique, postcreate_props_sql))
1209
1210        postexpression_props_sql = ""
1211        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
1212            postexpression_props_sql = self.properties(
1213                exp.Properties(
1214                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
1215                ),
1216                sep=" ",
1217                prefix=" ",
1218                wrapped=False,
1219            )
1220
1221        concurrently = " CONCURRENTLY" if expression.args.get("concurrently") else ""
1222        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
1223        no_schema_binding = (
1224            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
1225        )
1226
1227        clone = self.sql(expression, "clone")
1228        clone = f" {clone}" if clone else ""
1229
1230        if kind in self.EXPRESSION_PRECEDES_PROPERTIES_CREATABLES:
1231            properties_expression = f"{expression_sql}{properties_sql}"
1232        else:
1233            properties_expression = f"{properties_sql}{expression_sql}"
1234
1235        expression_sql = f"CREATE{modifiers} {kind}{concurrently}{exists_sql} {this}{properties_expression}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
1236        return self.prepend_ctes(expression, expression_sql)
def sequenceproperties_sql(self, expression: sqlglot.expressions.SequenceProperties) -> str:
1238    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
1239        start = self.sql(expression, "start")
1240        start = f"START WITH {start}" if start else ""
1241        increment = self.sql(expression, "increment")
1242        increment = f" INCREMENT BY {increment}" if increment else ""
1243        minvalue = self.sql(expression, "minvalue")
1244        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
1245        maxvalue = self.sql(expression, "maxvalue")
1246        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
1247        owned = self.sql(expression, "owned")
1248        owned = f" OWNED BY {owned}" if owned else ""
1249
1250        cache = expression.args.get("cache")
1251        if cache is None:
1252            cache_str = ""
1253        elif cache is True:
1254            cache_str = " CACHE"
1255        else:
1256            cache_str = f" CACHE {cache}"
1257
1258        options = self.expressions(expression, key="options", flat=True, sep=" ")
1259        options = f" {options}" if options else ""
1260
1261        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()
def clone_sql(self, expression: sqlglot.expressions.Clone) -> str:
1263    def clone_sql(self, expression: exp.Clone) -> str:
1264        this = self.sql(expression, "this")
1265        shallow = "SHALLOW " if expression.args.get("shallow") else ""
1266        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
1267        return f"{shallow}{keyword} {this}"
def describe_sql(self, expression: sqlglot.expressions.Describe) -> str:
1269    def describe_sql(self, expression: exp.Describe) -> str:
1270        style = expression.args.get("style")
1271        style = f" {style}" if style else ""
1272        partition = self.sql(expression, "partition")
1273        partition = f" {partition}" if partition else ""
1274        format = self.sql(expression, "format")
1275        format = f" {format}" if format else ""
1276
1277        return f"DESCRIBE{style}{format} {self.sql(expression, 'this')}{partition}"
def heredoc_sql(self, expression: sqlglot.expressions.Heredoc) -> str:
1279    def heredoc_sql(self, expression: exp.Heredoc) -> str:
1280        tag = self.sql(expression, "tag")
1281        return f"${tag}${self.sql(expression, 'this')}${tag}$"
def prepend_ctes(self, expression: sqlglot.expressions.Expression, sql: str) -> str:
1283    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
1284        with_ = self.sql(expression, "with")
1285        if with_:
1286            sql = f"{with_}{self.sep()}{sql}"
1287        return sql
def with_sql(self, expression: sqlglot.expressions.With) -> str:
1289    def with_sql(self, expression: exp.With) -> str:
1290        sql = self.expressions(expression, flat=True)
1291        recursive = (
1292            "RECURSIVE "
1293            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
1294            else ""
1295        )
1296        search = self.sql(expression, "search")
1297        search = f" {search}" if search else ""
1298
1299        return f"WITH {recursive}{sql}{search}"
def cte_sql(self, expression: sqlglot.expressions.CTE) -> str:
1301    def cte_sql(self, expression: exp.CTE) -> str:
1302        alias = expression.args.get("alias")
1303        if alias:
1304            alias.add_comments(expression.pop_comments())
1305
1306        alias_sql = self.sql(expression, "alias")
1307
1308        materialized = expression.args.get("materialized")
1309        if materialized is False:
1310            materialized = "NOT MATERIALIZED "
1311        elif materialized:
1312            materialized = "MATERIALIZED "
1313
1314        return f"{alias_sql} AS {materialized or ''}{self.wrap(expression)}"
def tablealias_sql(self, expression: sqlglot.expressions.TableAlias) -> str:
1316    def tablealias_sql(self, expression: exp.TableAlias) -> str:
1317        alias = self.sql(expression, "this")
1318        columns = self.expressions(expression, key="columns", flat=True)
1319        columns = f"({columns})" if columns else ""
1320
1321        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
1322            columns = ""
1323            self.unsupported("Named columns are not supported in table alias.")
1324
1325        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
1326            alias = self._next_name()
1327
1328        return f"{alias}{columns}"
def bitstring_sql(self, expression: sqlglot.expressions.BitString) -> str:
1330    def bitstring_sql(self, expression: exp.BitString) -> str:
1331        this = self.sql(expression, "this")
1332        if self.dialect.BIT_START:
1333            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
1334        return f"{int(this, 2)}"
def hexstring_sql( self, expression: sqlglot.expressions.HexString, binary_function_repr: Optional[str] = None) -> str:
1336    def hexstring_sql(
1337        self, expression: exp.HexString, binary_function_repr: t.Optional[str] = None
1338    ) -> str:
1339        this = self.sql(expression, "this")
1340        is_integer_type = expression.args.get("is_integer")
1341
1342        if (is_integer_type and not self.dialect.HEX_STRING_IS_INTEGER_TYPE) or (
1343            not self.dialect.HEX_START and not binary_function_repr
1344        ):
1345            # Integer representation will be returned if:
1346            # - The read dialect treats the hex value as integer literal but not the write
1347            # - The transpilation is not supported (write dialect hasn't set HEX_START or the param flag)
1348            return f"{int(this, 16)}"
1349
1350        if not is_integer_type:
1351            # Read dialect treats the hex value as BINARY/BLOB
1352            if binary_function_repr:
1353                # The write dialect supports the transpilation to its equivalent BINARY/BLOB
1354                return self.func(binary_function_repr, exp.Literal.string(this))
1355            if self.dialect.HEX_STRING_IS_INTEGER_TYPE:
1356                # The write dialect does not support the transpilation, it'll treat the hex value as INTEGER
1357                self.unsupported("Unsupported transpilation from BINARY/BLOB hex string")
1358
1359        return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
def bytestring_sql(self, expression: sqlglot.expressions.ByteString) -> str:
1361    def bytestring_sql(self, expression: exp.ByteString) -> str:
1362        this = self.sql(expression, "this")
1363        if self.dialect.BYTE_START:
1364            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
1365        return this
def unicodestring_sql(self, expression: sqlglot.expressions.UnicodeString) -> str:
1367    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
1368        this = self.sql(expression, "this")
1369        escape = expression.args.get("escape")
1370
1371        if self.dialect.UNICODE_START:
1372            escape_substitute = r"\\\1"
1373            left_quote, right_quote = self.dialect.UNICODE_START, self.dialect.UNICODE_END
1374        else:
1375            escape_substitute = r"\\u\1"
1376            left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END
1377
1378        if escape:
1379            escape_pattern = re.compile(rf"{escape.name}(\d+)")
1380            escape_sql = f" UESCAPE {self.sql(escape)}" if self.SUPPORTS_UESCAPE else ""
1381        else:
1382            escape_pattern = ESCAPED_UNICODE_RE
1383            escape_sql = ""
1384
1385        if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE):
1386            this = escape_pattern.sub(escape_substitute, this)
1387
1388        return f"{left_quote}{this}{right_quote}{escape_sql}"
def rawstring_sql(self, expression: sqlglot.expressions.RawString) -> str:
1390    def rawstring_sql(self, expression: exp.RawString) -> str:
1391        string = expression.this
1392        if "\\" in self.dialect.tokenizer_class.STRING_ESCAPES:
1393            string = string.replace("\\", "\\\\")
1394
1395        string = self.escape_str(string, escape_backslash=False)
1396        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
def datatypeparam_sql(self, expression: sqlglot.expressions.DataTypeParam) -> str:
1398    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
1399        this = self.sql(expression, "this")
1400        specifier = self.sql(expression, "expression")
1401        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
1402        return f"{this}{specifier}"
def datatype_sql(self, expression: sqlglot.expressions.DataType) -> str:
1404    def datatype_sql(self, expression: exp.DataType) -> str:
1405        nested = ""
1406        values = ""
1407        interior = self.expressions(expression, flat=True)
1408
1409        type_value = expression.this
1410        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
1411            type_sql = self.sql(expression, "kind")
1412        else:
1413            type_sql = (
1414                self.TYPE_MAPPING.get(type_value, type_value.value)
1415                if isinstance(type_value, exp.DataType.Type)
1416                else type_value
1417            )
1418
1419        if interior:
1420            if expression.args.get("nested"):
1421                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
1422                if expression.args.get("values") is not None:
1423                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
1424                    values = self.expressions(expression, key="values", flat=True)
1425                    values = f"{delimiters[0]}{values}{delimiters[1]}"
1426            elif type_value == exp.DataType.Type.INTERVAL:
1427                nested = f" {interior}"
1428            else:
1429                nested = f"({interior})"
1430
1431        type_sql = f"{type_sql}{nested}{values}"
1432        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
1433            exp.DataType.Type.TIMETZ,
1434            exp.DataType.Type.TIMESTAMPTZ,
1435        ):
1436            type_sql = f"{type_sql} WITH TIME ZONE"
1437
1438        return type_sql
def directory_sql(self, expression: sqlglot.expressions.Directory) -> str:
1440    def directory_sql(self, expression: exp.Directory) -> str:
1441        local = "LOCAL " if expression.args.get("local") else ""
1442        row_format = self.sql(expression, "row_format")
1443        row_format = f" {row_format}" if row_format else ""
1444        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
def delete_sql(self, expression: sqlglot.expressions.Delete) -> str:
1446    def delete_sql(self, expression: exp.Delete) -> str:
1447        this = self.sql(expression, "this")
1448        this = f" FROM {this}" if this else ""
1449        using = self.sql(expression, "using")
1450        using = f" USING {using}" if using else ""
1451        cluster = self.sql(expression, "cluster")
1452        cluster = f" {cluster}" if cluster else ""
1453        where = self.sql(expression, "where")
1454        returning = self.sql(expression, "returning")
1455        limit = self.sql(expression, "limit")
1456        tables = self.expressions(expression, key="tables")
1457        tables = f" {tables}" if tables else ""
1458        if self.RETURNING_END:
1459            expression_sql = f"{this}{using}{cluster}{where}{returning}{limit}"
1460        else:
1461            expression_sql = f"{returning}{this}{using}{cluster}{where}{limit}"
1462        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
def drop_sql(self, expression: sqlglot.expressions.Drop) -> str:
1464    def drop_sql(self, expression: exp.Drop) -> str:
1465        this = self.sql(expression, "this")
1466        expressions = self.expressions(expression, flat=True)
1467        expressions = f" ({expressions})" if expressions else ""
1468        kind = expression.args["kind"]
1469        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
1470        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
1471        concurrently_sql = " CONCURRENTLY" if expression.args.get("concurrently") else ""
1472        on_cluster = self.sql(expression, "cluster")
1473        on_cluster = f" {on_cluster}" if on_cluster else ""
1474        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1475        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
1476        cascade = " CASCADE" if expression.args.get("cascade") else ""
1477        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
1478        purge = " PURGE" if expression.args.get("purge") else ""
1479        return f"DROP{temporary}{materialized} {kind}{concurrently_sql}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"
def set_operation(self, expression: sqlglot.expressions.SetOperation) -> str:
1481    def set_operation(self, expression: exp.SetOperation) -> str:
1482        op_type = type(expression)
1483        op_name = op_type.key.upper()
1484
1485        distinct = expression.args.get("distinct")
1486        if (
1487            distinct is False
1488            and op_type in (exp.Except, exp.Intersect)
1489            and not self.EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE
1490        ):
1491            self.unsupported(f"{op_name} ALL is not supported")
1492
1493        default_distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[op_type]
1494
1495        if distinct is None:
1496            distinct = default_distinct
1497            if distinct is None:
1498                self.unsupported(f"{op_name} requires DISTINCT or ALL to be specified")
1499
1500        if distinct is default_distinct:
1501            distinct_or_all = ""
1502        else:
1503            distinct_or_all = " DISTINCT" if distinct else " ALL"
1504
1505        side_kind = " ".join(filter(None, [expression.side, expression.kind]))
1506        side_kind = f"{side_kind} " if side_kind else ""
1507
1508        by_name = " BY NAME" if expression.args.get("by_name") else ""
1509        on = self.expressions(expression, key="on", flat=True)
1510        on = f" ON ({on})" if on else ""
1511
1512        return f"{side_kind}{op_name}{distinct_or_all}{by_name}{on}"
def set_operations(self, expression: sqlglot.expressions.SetOperation) -> str:
1514    def set_operations(self, expression: exp.SetOperation) -> str:
1515        if not self.SET_OP_MODIFIERS:
1516            limit = expression.args.get("limit")
1517            order = expression.args.get("order")
1518
1519            if limit or order:
1520                select = self._move_ctes_to_top_level(
1521                    exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
1522                )
1523
1524                if limit:
1525                    select = select.limit(limit.pop(), copy=False)
1526                if order:
1527                    select = select.order_by(order.pop(), copy=False)
1528                return self.sql(select)
1529
1530        sqls: t.List[str] = []
1531        stack: t.List[t.Union[str, exp.Expression]] = [expression]
1532
1533        while stack:
1534            node = stack.pop()
1535
1536            if isinstance(node, exp.SetOperation):
1537                stack.append(node.expression)
1538                stack.append(
1539                    self.maybe_comment(
1540                        self.set_operation(node), comments=node.comments, separated=True
1541                    )
1542                )
1543                stack.append(node.this)
1544            else:
1545                sqls.append(self.sql(node))
1546
1547        this = self.sep().join(sqls)
1548        this = self.query_modifiers(expression, this)
1549        return self.prepend_ctes(expression, this)
def fetch_sql(self, expression: sqlglot.expressions.Fetch) -> str:
1551    def fetch_sql(self, expression: exp.Fetch) -> str:
1552        direction = expression.args.get("direction")
1553        direction = f" {direction}" if direction else ""
1554        count = self.sql(expression, "count")
1555        count = f" {count}" if count else ""
1556        limit_options = self.sql(expression, "limit_options")
1557        limit_options = f"{limit_options}" if limit_options else " ROWS ONLY"
1558        return f"{self.seg('FETCH')}{direction}{count}{limit_options}"
def limitoptions_sql(self, expression: sqlglot.expressions.LimitOptions) -> str:
1560    def limitoptions_sql(self, expression: exp.LimitOptions) -> str:
1561        percent = " PERCENT" if expression.args.get("percent") else ""
1562        rows = " ROWS" if expression.args.get("rows") else ""
1563        with_ties = " WITH TIES" if expression.args.get("with_ties") else ""
1564        if not with_ties and rows:
1565            with_ties = " ONLY"
1566        return f"{percent}{rows}{with_ties}"
def filter_sql(self, expression: sqlglot.expressions.Filter) -> str:
1568    def filter_sql(self, expression: exp.Filter) -> str:
1569        if self.AGGREGATE_FILTER_SUPPORTED:
1570            this = self.sql(expression, "this")
1571            where = self.sql(expression, "expression").strip()
1572            return f"{this} FILTER({where})"
1573
1574        agg = expression.this
1575        agg_arg = agg.this
1576        cond = expression.expression.this
1577        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
1578        return self.sql(agg)
def hint_sql(self, expression: sqlglot.expressions.Hint) -> str:
1580    def hint_sql(self, expression: exp.Hint) -> str:
1581        if not self.QUERY_HINTS:
1582            self.unsupported("Hints are not supported")
1583            return ""
1584
1585        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
def indexparameters_sql(self, expression: sqlglot.expressions.IndexParameters) -> str:
1587    def indexparameters_sql(self, expression: exp.IndexParameters) -> str:
1588        using = self.sql(expression, "using")
1589        using = f" USING {using}" if using else ""
1590        columns = self.expressions(expression, key="columns", flat=True)
1591        columns = f"({columns})" if columns else ""
1592        partition_by = self.expressions(expression, key="partition_by", flat=True)
1593        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
1594        where = self.sql(expression, "where")
1595        include = self.expressions(expression, key="include", flat=True)
1596        if include:
1597            include = f" INCLUDE ({include})"
1598        with_storage = self.expressions(expression, key="with_storage", flat=True)
1599        with_storage = f" WITH ({with_storage})" if with_storage else ""
1600        tablespace = self.sql(expression, "tablespace")
1601        tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
1602        on = self.sql(expression, "on")
1603        on = f" ON {on}" if on else ""
1604
1605        return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}"
def index_sql(self, expression: sqlglot.expressions.Index) -> str:
1607    def index_sql(self, expression: exp.Index) -> str:
1608        unique = "UNIQUE " if expression.args.get("unique") else ""
1609        primary = "PRIMARY " if expression.args.get("primary") else ""
1610        amp = "AMP " if expression.args.get("amp") else ""
1611        name = self.sql(expression, "this")
1612        name = f"{name} " if name else ""
1613        table = self.sql(expression, "table")
1614        table = f"{self.INDEX_ON} {table}" if table else ""
1615
1616        index = "INDEX " if not table else ""
1617
1618        params = self.sql(expression, "params")
1619        return f"{unique}{primary}{amp}{index}{name}{table}{params}"
def identifier_sql(self, expression: sqlglot.expressions.Identifier) -> str:
1621    def identifier_sql(self, expression: exp.Identifier) -> str:
1622        text = expression.name
1623        lower = text.lower()
1624        text = lower if self.normalize and not expression.quoted else text
1625        text = text.replace(self._identifier_end, self._escaped_identifier_end)
1626        if (
1627            expression.quoted
1628            or self.dialect.can_identify(text, self.identify)
1629            or lower in self.RESERVED_KEYWORDS
1630            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
1631        ):
1632            text = f"{self._identifier_start}{text}{self._identifier_end}"
1633        return text
def hex_sql(self, expression: sqlglot.expressions.Hex) -> str:
1635    def hex_sql(self, expression: exp.Hex) -> str:
1636        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
1637        if self.dialect.HEX_LOWERCASE:
1638            text = self.func("LOWER", text)
1639
1640        return text
def lowerhex_sql(self, expression: sqlglot.expressions.LowerHex) -> str:
1642    def lowerhex_sql(self, expression: exp.LowerHex) -> str:
1643        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
1644        if not self.dialect.HEX_LOWERCASE:
1645            text = self.func("LOWER", text)
1646        return text
def inputoutputformat_sql(self, expression: sqlglot.expressions.InputOutputFormat) -> str:
1648    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
1649        input_format = self.sql(expression, "input_format")
1650        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
1651        output_format = self.sql(expression, "output_format")
1652        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
1653        return self.sep().join((input_format, output_format))
def national_sql(self, expression: sqlglot.expressions.National, prefix: str = 'N') -> str:
1655    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
1656        string = self.sql(exp.Literal.string(expression.name))
1657        return f"{prefix}{string}"
def partition_sql(self, expression: sqlglot.expressions.Partition) -> str:
1659    def partition_sql(self, expression: exp.Partition) -> str:
1660        partition_keyword = "SUBPARTITION" if expression.args.get("subpartition") else "PARTITION"
1661        return f"{partition_keyword}({self.expressions(expression, flat=True)})"
def properties_sql(self, expression: sqlglot.expressions.Properties) -> str:
1663    def properties_sql(self, expression: exp.Properties) -> str:
1664        root_properties = []
1665        with_properties = []
1666
1667        for p in expression.expressions:
1668            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1669            if p_loc == exp.Properties.Location.POST_WITH:
1670                with_properties.append(p)
1671            elif p_loc == exp.Properties.Location.POST_SCHEMA:
1672                root_properties.append(p)
1673
1674        root_props_ast = exp.Properties(expressions=root_properties)
1675        root_props_ast.parent = expression.parent
1676
1677        with_props_ast = exp.Properties(expressions=with_properties)
1678        with_props_ast.parent = expression.parent
1679
1680        root_props = self.root_properties(root_props_ast)
1681        with_props = self.with_properties(with_props_ast)
1682
1683        if root_props and with_props and not self.pretty:
1684            with_props = " " + with_props
1685
1686        return root_props + with_props
def root_properties(self, properties: sqlglot.expressions.Properties) -> str:
1688    def root_properties(self, properties: exp.Properties) -> str:
1689        if properties.expressions:
1690            return self.expressions(properties, indent=False, sep=" ")
1691        return ""
def properties( self, properties: sqlglot.expressions.Properties, prefix: str = '', sep: str = ', ', suffix: str = '', wrapped: bool = True) -> str:
1693    def properties(
1694        self,
1695        properties: exp.Properties,
1696        prefix: str = "",
1697        sep: str = ", ",
1698        suffix: str = "",
1699        wrapped: bool = True,
1700    ) -> str:
1701        if properties.expressions:
1702            expressions = self.expressions(properties, sep=sep, indent=False)
1703            if expressions:
1704                expressions = self.wrap(expressions) if wrapped else expressions
1705                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
1706        return ""
def with_properties(self, properties: sqlglot.expressions.Properties) -> str:
1708    def with_properties(self, properties: exp.Properties) -> str:
1709        return self.properties(properties, prefix=self.seg(self.WITH_PROPERTIES_PREFIX, sep=""))
def locate_properties(self, properties: sqlglot.expressions.Properties) -> DefaultDict:
1711    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
1712        properties_locs = defaultdict(list)
1713        for p in properties.expressions:
1714            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1715            if p_loc != exp.Properties.Location.UNSUPPORTED:
1716                properties_locs[p_loc].append(p)
1717            else:
1718                self.unsupported(f"Unsupported property {p.key}")
1719
1720        return properties_locs
def property_name( self, expression: sqlglot.expressions.Property, string_key: bool = False) -> str:
1722    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
1723        if isinstance(expression.this, exp.Dot):
1724            return self.sql(expression, "this")
1725        return f"'{expression.name}'" if string_key else expression.name
def property_sql(self, expression: sqlglot.expressions.Property) -> str:
1727    def property_sql(self, expression: exp.Property) -> str:
1728        property_cls = expression.__class__
1729        if property_cls == exp.Property:
1730            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
1731
1732        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
1733        if not property_name:
1734            self.unsupported(f"Unsupported property {expression.key}")
1735
1736        return f"{property_name}={self.sql(expression, 'this')}"
def likeproperty_sql(self, expression: sqlglot.expressions.LikeProperty) -> str:
1738    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
1739        if self.SUPPORTS_CREATE_TABLE_LIKE:
1740            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
1741            options = f" {options}" if options else ""
1742
1743            like = f"LIKE {self.sql(expression, 'this')}{options}"
1744            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
1745                like = f"({like})"
1746
1747            return like
1748
1749        if expression.expressions:
1750            self.unsupported("Transpilation of LIKE property options is unsupported")
1751
1752        select = exp.select("*").from_(expression.this).limit(0)
1753        return f"AS {self.sql(select)}"
def fallbackproperty_sql(self, expression: sqlglot.expressions.FallbackProperty) -> str:
1755    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
1756        no = "NO " if expression.args.get("no") else ""
1757        protection = " PROTECTION" if expression.args.get("protection") else ""
1758        return f"{no}FALLBACK{protection}"
def journalproperty_sql(self, expression: sqlglot.expressions.JournalProperty) -> str:
1760    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
1761        no = "NO " if expression.args.get("no") else ""
1762        local = expression.args.get("local")
1763        local = f"{local} " if local else ""
1764        dual = "DUAL " if expression.args.get("dual") else ""
1765        before = "BEFORE " if expression.args.get("before") else ""
1766        after = "AFTER " if expression.args.get("after") else ""
1767        return f"{no}{local}{dual}{before}{after}JOURNAL"
def freespaceproperty_sql(self, expression: sqlglot.expressions.FreespaceProperty) -> str:
1769    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
1770        freespace = self.sql(expression, "this")
1771        percent = " PERCENT" if expression.args.get("percent") else ""
1772        return f"FREESPACE={freespace}{percent}"
def checksumproperty_sql(self, expression: sqlglot.expressions.ChecksumProperty) -> str:
1774    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
1775        if expression.args.get("default"):
1776            property = "DEFAULT"
1777        elif expression.args.get("on"):
1778            property = "ON"
1779        else:
1780            property = "OFF"
1781        return f"CHECKSUM={property}"
def mergeblockratioproperty_sql(self, expression: sqlglot.expressions.MergeBlockRatioProperty) -> str:
1783    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
1784        if expression.args.get("no"):
1785            return "NO MERGEBLOCKRATIO"
1786        if expression.args.get("default"):
1787            return "DEFAULT MERGEBLOCKRATIO"
1788
1789        percent = " PERCENT" if expression.args.get("percent") else ""
1790        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
def datablocksizeproperty_sql(self, expression: sqlglot.expressions.DataBlocksizeProperty) -> str:
1792    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
1793        default = expression.args.get("default")
1794        minimum = expression.args.get("minimum")
1795        maximum = expression.args.get("maximum")
1796        if default or minimum or maximum:
1797            if default:
1798                prop = "DEFAULT"
1799            elif minimum:
1800                prop = "MINIMUM"
1801            else:
1802                prop = "MAXIMUM"
1803            return f"{prop} DATABLOCKSIZE"
1804        units = expression.args.get("units")
1805        units = f" {units}" if units else ""
1806        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
def blockcompressionproperty_sql(self, expression: sqlglot.expressions.BlockCompressionProperty) -> str:
1808    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
1809        autotemp = expression.args.get("autotemp")
1810        always = expression.args.get("always")
1811        default = expression.args.get("default")
1812        manual = expression.args.get("manual")
1813        never = expression.args.get("never")
1814
1815        if autotemp is not None:
1816            prop = f"AUTOTEMP({self.expressions(autotemp)})"
1817        elif always:
1818            prop = "ALWAYS"
1819        elif default:
1820            prop = "DEFAULT"
1821        elif manual:
1822            prop = "MANUAL"
1823        elif never:
1824            prop = "NEVER"
1825        return f"BLOCKCOMPRESSION={prop}"
def isolatedloadingproperty_sql(self, expression: sqlglot.expressions.IsolatedLoadingProperty) -> str:
1827    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
1828        no = expression.args.get("no")
1829        no = " NO" if no else ""
1830        concurrent = expression.args.get("concurrent")
1831        concurrent = " CONCURRENT" if concurrent else ""
1832        target = self.sql(expression, "target")
1833        target = f" {target}" if target else ""
1834        return f"WITH{no}{concurrent} ISOLATED LOADING{target}"
def partitionboundspec_sql(self, expression: sqlglot.expressions.PartitionBoundSpec) -> str:
1836    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
1837        if isinstance(expression.this, list):
1838            return f"IN ({self.expressions(expression, key='this', flat=True)})"
1839        if expression.this:
1840            modulus = self.sql(expression, "this")
1841            remainder = self.sql(expression, "expression")
1842            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
1843
1844        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
1845        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
1846        return f"FROM ({from_expressions}) TO ({to_expressions})"
def partitionedofproperty_sql(self, expression: sqlglot.expressions.PartitionedOfProperty) -> str:
1848    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
1849        this = self.sql(expression, "this")
1850
1851        for_values_or_default = expression.expression
1852        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
1853            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
1854        else:
1855            for_values_or_default = " DEFAULT"
1856
1857        return f"PARTITION OF {this}{for_values_or_default}"
def lockingproperty_sql(self, expression: sqlglot.expressions.LockingProperty) -> str:
1859    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
1860        kind = expression.args.get("kind")
1861        this = f" {self.sql(expression, 'this')}" if expression.this else ""
1862        for_or_in = expression.args.get("for_or_in")
1863        for_or_in = f" {for_or_in}" if for_or_in else ""
1864        lock_type = expression.args.get("lock_type")
1865        override = " OVERRIDE" if expression.args.get("override") else ""
1866        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
def withdataproperty_sql(self, expression: sqlglot.expressions.WithDataProperty) -> str:
1868    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
1869        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
1870        statistics = expression.args.get("statistics")
1871        statistics_sql = ""
1872        if statistics is not None:
1873            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
1874        return f"{data_sql}{statistics_sql}"
def withsystemversioningproperty_sql( self, expression: sqlglot.expressions.WithSystemVersioningProperty) -> str:
1876    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
1877        this = self.sql(expression, "this")
1878        this = f"HISTORY_TABLE={this}" if this else ""
1879        data_consistency: t.Optional[str] = self.sql(expression, "data_consistency")
1880        data_consistency = (
1881            f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None
1882        )
1883        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
1884        retention_period = (
1885            f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None
1886        )
1887
1888        if this:
1889            on_sql = self.func("ON", this, data_consistency, retention_period)
1890        else:
1891            on_sql = "ON" if expression.args.get("on") else "OFF"
1892
1893        sql = f"SYSTEM_VERSIONING={on_sql}"
1894
1895        return f"WITH({sql})" if expression.args.get("with") else sql
def insert_sql(self, expression: sqlglot.expressions.Insert) -> str:
1897    def insert_sql(self, expression: exp.Insert) -> str:
1898        hint = self.sql(expression, "hint")
1899        overwrite = expression.args.get("overwrite")
1900
1901        if isinstance(expression.this, exp.Directory):
1902            this = " OVERWRITE" if overwrite else " INTO"
1903        else:
1904            this = self.INSERT_OVERWRITE if overwrite else " INTO"
1905
1906        stored = self.sql(expression, "stored")
1907        stored = f" {stored}" if stored else ""
1908        alternative = expression.args.get("alternative")
1909        alternative = f" OR {alternative}" if alternative else ""
1910        ignore = " IGNORE" if expression.args.get("ignore") else ""
1911        is_function = expression.args.get("is_function")
1912        if is_function:
1913            this = f"{this} FUNCTION"
1914        this = f"{this} {self.sql(expression, 'this')}"
1915
1916        exists = " IF EXISTS" if expression.args.get("exists") else ""
1917        where = self.sql(expression, "where")
1918        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
1919        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
1920        on_conflict = self.sql(expression, "conflict")
1921        on_conflict = f" {on_conflict}" if on_conflict else ""
1922        by_name = " BY NAME" if expression.args.get("by_name") else ""
1923        returning = self.sql(expression, "returning")
1924
1925        if self.RETURNING_END:
1926            expression_sql = f"{expression_sql}{on_conflict}{returning}"
1927        else:
1928            expression_sql = f"{returning}{expression_sql}{on_conflict}"
1929
1930        partition_by = self.sql(expression, "partition")
1931        partition_by = f" {partition_by}" if partition_by else ""
1932        settings = self.sql(expression, "settings")
1933        settings = f" {settings}" if settings else ""
1934
1935        source = self.sql(expression, "source")
1936        source = f"TABLE {source}" if source else ""
1937
1938        sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{partition_by}{settings}{where}{expression_sql}{source}"
1939        return self.prepend_ctes(expression, sql)
def introducer_sql(self, expression: sqlglot.expressions.Introducer) -> str:
1941    def introducer_sql(self, expression: exp.Introducer) -> str:
1942        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
def kill_sql(self, expression: sqlglot.expressions.Kill) -> str:
1944    def kill_sql(self, expression: exp.Kill) -> str:
1945        kind = self.sql(expression, "kind")
1946        kind = f" {kind}" if kind else ""
1947        this = self.sql(expression, "this")
1948        this = f" {this}" if this else ""
1949        return f"KILL{kind}{this}"
def pseudotype_sql(self, expression: sqlglot.expressions.PseudoType) -> str:
1951    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
1952        return expression.name
def objectidentifier_sql(self, expression: sqlglot.expressions.ObjectIdentifier) -> str:
1954    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
1955        return expression.name
def onconflict_sql(self, expression: sqlglot.expressions.OnConflict) -> str:
1957    def onconflict_sql(self, expression: exp.OnConflict) -> str:
1958        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
1959
1960        constraint = self.sql(expression, "constraint")
1961        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
1962
1963        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
1964        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
1965        action = self.sql(expression, "action")
1966
1967        expressions = self.expressions(expression, flat=True)
1968        if expressions:
1969            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
1970            expressions = f" {set_keyword}{expressions}"
1971
1972        where = self.sql(expression, "where")
1973        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}{where}"
def returning_sql(self, expression: sqlglot.expressions.Returning) -> str:
1975    def returning_sql(self, expression: exp.Returning) -> str:
1976        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
def rowformatdelimitedproperty_sql(self, expression: sqlglot.expressions.RowFormatDelimitedProperty) -> str:
1978    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
1979        fields = self.sql(expression, "fields")
1980        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
1981        escaped = self.sql(expression, "escaped")
1982        escaped = f" ESCAPED BY {escaped}" if escaped else ""
1983        items = self.sql(expression, "collection_items")
1984        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
1985        keys = self.sql(expression, "map_keys")
1986        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
1987        lines = self.sql(expression, "lines")
1988        lines = f" LINES TERMINATED BY {lines}" if lines else ""
1989        null = self.sql(expression, "null")
1990        null = f" NULL DEFINED AS {null}" if null else ""
1991        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
def withtablehint_sql(self, expression: sqlglot.expressions.WithTableHint) -> str:
1993    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
1994        return f"WITH ({self.expressions(expression, flat=True)})"
def indextablehint_sql(self, expression: sqlglot.expressions.IndexTableHint) -> str:
1996    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
1997        this = f"{self.sql(expression, 'this')} INDEX"
1998        target = self.sql(expression, "target")
1999        target = f" FOR {target}" if target else ""
2000        return f"{this}{target} ({self.expressions(expression, flat=True)})"
def historicaldata_sql(self, expression: sqlglot.expressions.HistoricalData) -> str:
2002    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
2003        this = self.sql(expression, "this")
2004        kind = self.sql(expression, "kind")
2005        expr = self.sql(expression, "expression")
2006        return f"{this} ({kind} => {expr})"
def table_parts(self, expression: sqlglot.expressions.Table) -> str:
2008    def table_parts(self, expression: exp.Table) -> str:
2009        return ".".join(
2010            self.sql(part)
2011            for part in (
2012                expression.args.get("catalog"),
2013                expression.args.get("db"),
2014                expression.args.get("this"),
2015            )
2016            if part is not None
2017        )
def table_sql(self, expression: sqlglot.expressions.Table, sep: str = ' AS ') -> str:
2019    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
2020        table = self.table_parts(expression)
2021        only = "ONLY " if expression.args.get("only") else ""
2022        partition = self.sql(expression, "partition")
2023        partition = f" {partition}" if partition else ""
2024        version = self.sql(expression, "version")
2025        version = f" {version}" if version else ""
2026        alias = self.sql(expression, "alias")
2027        alias = f"{sep}{alias}" if alias else ""
2028
2029        sample = self.sql(expression, "sample")
2030        if self.dialect.ALIAS_POST_TABLESAMPLE:
2031            sample_pre_alias = sample
2032            sample_post_alias = ""
2033        else:
2034            sample_pre_alias = ""
2035            sample_post_alias = sample
2036
2037        hints = self.expressions(expression, key="hints", sep=" ")
2038        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
2039        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2040        joins = self.indent(
2041            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
2042        )
2043        laterals = self.expressions(expression, key="laterals", sep="")
2044
2045        file_format = self.sql(expression, "format")
2046        if file_format:
2047            pattern = self.sql(expression, "pattern")
2048            pattern = f", PATTERN => {pattern}" if pattern else ""
2049            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
2050
2051        ordinality = expression.args.get("ordinality") or ""
2052        if ordinality:
2053            ordinality = f" WITH ORDINALITY{alias}"
2054            alias = ""
2055
2056        when = self.sql(expression, "when")
2057        if when:
2058            table = f"{table} {when}"
2059
2060        changes = self.sql(expression, "changes")
2061        changes = f" {changes}" if changes else ""
2062
2063        rows_from = self.expressions(expression, key="rows_from")
2064        if rows_from:
2065            table = f"ROWS FROM {self.wrap(rows_from)}"
2066
2067        return f"{only}{table}{changes}{partition}{version}{file_format}{sample_pre_alias}{alias}{hints}{pivots}{sample_post_alias}{joins}{laterals}{ordinality}"
def tablefromrows_sql(self, expression: sqlglot.expressions.TableFromRows) -> str:
2069    def tablefromrows_sql(self, expression: exp.TableFromRows) -> str:
2070        table = self.func("TABLE", expression.this)
2071        alias = self.sql(expression, "alias")
2072        alias = f" AS {alias}" if alias else ""
2073        sample = self.sql(expression, "sample")
2074        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2075        joins = self.indent(
2076            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
2077        )
2078        return f"{table}{alias}{pivots}{sample}{joins}"
def tablesample_sql( self, expression: sqlglot.expressions.TableSample, tablesample_keyword: Optional[str] = None) -> str:
2080    def tablesample_sql(
2081        self,
2082        expression: exp.TableSample,
2083        tablesample_keyword: t.Optional[str] = None,
2084    ) -> str:
2085        method = self.sql(expression, "method")
2086        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
2087        numerator = self.sql(expression, "bucket_numerator")
2088        denominator = self.sql(expression, "bucket_denominator")
2089        field = self.sql(expression, "bucket_field")
2090        field = f" ON {field}" if field else ""
2091        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
2092        seed = self.sql(expression, "seed")
2093        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
2094
2095        size = self.sql(expression, "size")
2096        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
2097            size = f"{size} ROWS"
2098
2099        percent = self.sql(expression, "percent")
2100        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
2101            percent = f"{percent} PERCENT"
2102
2103        expr = f"{bucket}{percent}{size}"
2104        if self.TABLESAMPLE_REQUIRES_PARENS:
2105            expr = f"({expr})"
2106
2107        return f" {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}"
def pivot_sql(self, expression: sqlglot.expressions.Pivot) -> str:
2109    def pivot_sql(self, expression: exp.Pivot) -> str:
2110        expressions = self.expressions(expression, flat=True)
2111        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
2112
2113        group = self.sql(expression, "group")
2114
2115        if expression.this:
2116            this = self.sql(expression, "this")
2117            if not expressions:
2118                return f"UNPIVOT {this}"
2119
2120            on = f"{self.seg('ON')} {expressions}"
2121            into = self.sql(expression, "into")
2122            into = f"{self.seg('INTO')} {into}" if into else ""
2123            using = self.expressions(expression, key="using", flat=True)
2124            using = f"{self.seg('USING')} {using}" if using else ""
2125            return f"{direction} {this}{on}{into}{using}{group}"
2126
2127        alias = self.sql(expression, "alias")
2128        alias = f" AS {alias}" if alias else ""
2129
2130        fields = self.expressions(
2131            expression,
2132            "fields",
2133            sep=" ",
2134            dynamic=True,
2135            new_line=True,
2136            skip_first=True,
2137            skip_last=True,
2138        )
2139
2140        include_nulls = expression.args.get("include_nulls")
2141        if include_nulls is not None:
2142            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
2143        else:
2144            nulls = ""
2145
2146        default_on_null = self.sql(expression, "default_on_null")
2147        default_on_null = f" DEFAULT ON NULL ({default_on_null})" if default_on_null else ""
2148        return f"{self.seg(direction)}{nulls}({expressions} FOR {fields}{default_on_null}{group}){alias}"
def version_sql(self, expression: sqlglot.expressions.Version) -> str:
2150    def version_sql(self, expression: exp.Version) -> str:
2151        this = f"FOR {expression.name}"
2152        kind = expression.text("kind")
2153        expr = self.sql(expression, "expression")
2154        return f"{this} {kind} {expr}"
def tuple_sql(self, expression: sqlglot.expressions.Tuple) -> str:
2156    def tuple_sql(self, expression: exp.Tuple) -> str:
2157        return f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
def update_sql(self, expression: sqlglot.expressions.Update) -> str:
2159    def update_sql(self, expression: exp.Update) -> str:
2160        this = self.sql(expression, "this")
2161        set_sql = self.expressions(expression, flat=True)
2162        from_sql = self.sql(expression, "from")
2163        where_sql = self.sql(expression, "where")
2164        returning = self.sql(expression, "returning")
2165        order = self.sql(expression, "order")
2166        limit = self.sql(expression, "limit")
2167        if self.RETURNING_END:
2168            expression_sql = f"{from_sql}{where_sql}{returning}"
2169        else:
2170            expression_sql = f"{returning}{from_sql}{where_sql}"
2171        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
2172        return self.prepend_ctes(expression, sql)
def values_sql( self, expression: sqlglot.expressions.Values, values_as_table: bool = True) -> str:
2174    def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str:
2175        values_as_table = values_as_table and self.VALUES_AS_TABLE
2176
2177        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
2178        if values_as_table or not expression.find_ancestor(exp.From, exp.Join):
2179            args = self.expressions(expression)
2180            alias = self.sql(expression, "alias")
2181            values = f"VALUES{self.seg('')}{args}"
2182            values = (
2183                f"({values})"
2184                if self.WRAP_DERIVED_VALUES
2185                and (alias or isinstance(expression.parent, (exp.From, exp.Table)))
2186                else values
2187            )
2188            return f"{values} AS {alias}" if alias else values
2189
2190        # Converts `VALUES...` expression into a series of select unions.
2191        alias_node = expression.args.get("alias")
2192        column_names = alias_node and alias_node.columns
2193
2194        selects: t.List[exp.Query] = []
2195
2196        for i, tup in enumerate(expression.expressions):
2197            row = tup.expressions
2198
2199            if i == 0 and column_names:
2200                row = [
2201                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
2202                ]
2203
2204            selects.append(exp.Select(expressions=row))
2205
2206        if self.pretty:
2207            # This may result in poor performance for large-cardinality `VALUES` tables, due to
2208            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
2209            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
2210            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
2211            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
2212
2213        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
2214        unions = " UNION ALL ".join(self.sql(select) for select in selects)
2215        return f"({unions}){alias}"
def var_sql(self, expression: sqlglot.expressions.Var) -> str:
2217    def var_sql(self, expression: exp.Var) -> str:
2218        return self.sql(expression, "this")
@unsupported_args('expressions')
def into_sql(self, expression: sqlglot.expressions.Into) -> str:
2220    @unsupported_args("expressions")
2221    def into_sql(self, expression: exp.Into) -> str:
2222        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
2223        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
2224        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
def from_sql(self, expression: sqlglot.expressions.From) -> str:
2226    def from_sql(self, expression: exp.From) -> str:
2227        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
def groupingsets_sql(self, expression: sqlglot.expressions.GroupingSets) -> str:
2229    def groupingsets_sql(self, expression: exp.GroupingSets) -> str:
2230        grouping_sets = self.expressions(expression, indent=False)
2231        return f"GROUPING SETS {self.wrap(grouping_sets)}"
def rollup_sql(self, expression: sqlglot.expressions.Rollup) -> str:
2233    def rollup_sql(self, expression: exp.Rollup) -> str:
2234        expressions = self.expressions(expression, indent=False)
2235        return f"ROLLUP {self.wrap(expressions)}" if expressions else "WITH ROLLUP"
def cube_sql(self, expression: sqlglot.expressions.Cube) -> str:
2237    def cube_sql(self, expression: exp.Cube) -> str:
2238        expressions = self.expressions(expression, indent=False)
2239        return f"CUBE {self.wrap(expressions)}" if expressions else "WITH CUBE"
def group_sql(self, expression: sqlglot.expressions.Group) -> str:
2241    def group_sql(self, expression: exp.Group) -> str:
2242        group_by_all = expression.args.get("all")
2243        if group_by_all is True:
2244            modifier = " ALL"
2245        elif group_by_all is False:
2246            modifier = " DISTINCT"
2247        else:
2248            modifier = ""
2249
2250        group_by = self.op_expressions(f"GROUP BY{modifier}", expression)
2251
2252        grouping_sets = self.expressions(expression, key="grouping_sets")
2253        cube = self.expressions(expression, key="cube")
2254        rollup = self.expressions(expression, key="rollup")
2255
2256        groupings = csv(
2257            self.seg(grouping_sets) if grouping_sets else "",
2258            self.seg(cube) if cube else "",
2259            self.seg(rollup) if rollup else "",
2260            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
2261            sep=self.GROUPINGS_SEP,
2262        )
2263
2264        if (
2265            expression.expressions
2266            and groupings
2267            and groupings.strip() not in ("WITH CUBE", "WITH ROLLUP")
2268        ):
2269            group_by = f"{group_by}{self.GROUPINGS_SEP}"
2270
2271        return f"{group_by}{groupings}"
def having_sql(self, expression: sqlglot.expressions.Having) -> str:
2273    def having_sql(self, expression: exp.Having) -> str:
2274        this = self.indent(self.sql(expression, "this"))
2275        return f"{self.seg('HAVING')}{self.sep()}{this}"
def connect_sql(self, expression: sqlglot.expressions.Connect) -> str:
2277    def connect_sql(self, expression: exp.Connect) -> str:
2278        start = self.sql(expression, "start")
2279        start = self.seg(f"START WITH {start}") if start else ""
2280        nocycle = " NOCYCLE" if expression.args.get("nocycle") else ""
2281        connect = self.sql(expression, "connect")
2282        connect = self.seg(f"CONNECT BY{nocycle} {connect}")
2283        return start + connect
def prior_sql(self, expression: sqlglot.expressions.Prior) -> str:
2285    def prior_sql(self, expression: exp.Prior) -> str:
2286        return f"PRIOR {self.sql(expression, 'this')}"
def join_sql(self, expression: sqlglot.expressions.Join) -> str:
2288    def join_sql(self, expression: exp.Join) -> str:
2289        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
2290            side = None
2291        else:
2292            side = expression.side
2293
2294        op_sql = " ".join(
2295            op
2296            for op in (
2297                expression.method,
2298                "GLOBAL" if expression.args.get("global") else None,
2299                side,
2300                expression.kind,
2301                expression.hint if self.JOIN_HINTS else None,
2302            )
2303            if op
2304        )
2305        match_cond = self.sql(expression, "match_condition")
2306        match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
2307        on_sql = self.sql(expression, "on")
2308        using = expression.args.get("using")
2309
2310        if not on_sql and using:
2311            on_sql = csv(*(self.sql(column) for column in using))
2312
2313        this = expression.this
2314        this_sql = self.sql(this)
2315
2316        exprs = self.expressions(expression)
2317        if exprs:
2318            this_sql = f"{this_sql},{self.seg(exprs)}"
2319
2320        if on_sql:
2321            on_sql = self.indent(on_sql, skip_first=True)
2322            space = self.seg(" " * self.pad) if self.pretty else " "
2323            if using:
2324                on_sql = f"{space}USING ({on_sql})"
2325            else:
2326                on_sql = f"{space}ON {on_sql}"
2327        elif not op_sql:
2328            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
2329                return f" {this_sql}"
2330
2331            return f", {this_sql}"
2332
2333        if op_sql != "STRAIGHT_JOIN":
2334            op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
2335
2336        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2337        return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}{pivots}"
def lambda_sql( self, expression: sqlglot.expressions.Lambda, arrow_sep: str = '->', wrap: bool = True) -> str:
2339    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->", wrap: bool = True) -> str:
2340        args = self.expressions(expression, flat=True)
2341        args = f"({args})" if wrap and len(args.split(",")) > 1 else args
2342        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
def lateral_op(self, expression: sqlglot.expressions.Lateral) -> str:
2344    def lateral_op(self, expression: exp.Lateral) -> str:
2345        cross_apply = expression.args.get("cross_apply")
2346
2347        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
2348        if cross_apply is True:
2349            op = "INNER JOIN "
2350        elif cross_apply is False:
2351            op = "LEFT JOIN "
2352        else:
2353            op = ""
2354
2355        return f"{op}LATERAL"
def lateral_sql(self, expression: sqlglot.expressions.Lateral) -> str:
2357    def lateral_sql(self, expression: exp.Lateral) -> str:
2358        this = self.sql(expression, "this")
2359
2360        if expression.args.get("view"):
2361            alias = expression.args["alias"]
2362            columns = self.expressions(alias, key="columns", flat=True)
2363            table = f" {alias.name}" if alias.name else ""
2364            columns = f" AS {columns}" if columns else ""
2365            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
2366            return f"{op_sql}{self.sep()}{this}{table}{columns}"
2367
2368        alias = self.sql(expression, "alias")
2369        alias = f" AS {alias}" if alias else ""
2370
2371        ordinality = expression.args.get("ordinality") or ""
2372        if ordinality:
2373            ordinality = f" WITH ORDINALITY{alias}"
2374            alias = ""
2375
2376        return f"{self.lateral_op(expression)} {this}{alias}{ordinality}"
def limit_sql(self, expression: sqlglot.expressions.Limit, top: bool = False) -> str:
2378    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
2379        this = self.sql(expression, "this")
2380
2381        args = [
2382            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
2383            for e in (expression.args.get(k) for k in ("offset", "expression"))
2384            if e
2385        ]
2386
2387        args_sql = ", ".join(self.sql(e) for e in args)
2388        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
2389        expressions = self.expressions(expression, flat=True)
2390        limit_options = self.sql(expression, "limit_options")
2391        expressions = f" BY {expressions}" if expressions else ""
2392
2393        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{limit_options}{expressions}"
def offset_sql(self, expression: sqlglot.expressions.Offset) -> str:
2395    def offset_sql(self, expression: exp.Offset) -> str:
2396        this = self.sql(expression, "this")
2397        value = expression.expression
2398        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
2399        expressions = self.expressions(expression, flat=True)
2400        expressions = f" BY {expressions}" if expressions else ""
2401        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
def setitem_sql(self, expression: sqlglot.expressions.SetItem) -> str:
2403    def setitem_sql(self, expression: exp.SetItem) -> str:
2404        kind = self.sql(expression, "kind")
2405        kind = f"{kind} " if kind else ""
2406        this = self.sql(expression, "this")
2407        expressions = self.expressions(expression)
2408        collate = self.sql(expression, "collate")
2409        collate = f" COLLATE {collate}" if collate else ""
2410        global_ = "GLOBAL " if expression.args.get("global") else ""
2411        return f"{global_}{kind}{this}{expressions}{collate}"
def set_sql(self, expression: sqlglot.expressions.Set) -> str:
2413    def set_sql(self, expression: exp.Set) -> str:
2414        expressions = f" {self.expressions(expression, flat=True)}"
2415        tag = " TAG" if expression.args.get("tag") else ""
2416        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
def queryband_sql(self, expression: sqlglot.expressions.QueryBand) -> str:
2418    def queryband_sql(self, expression: exp.QueryBand) -> str:
2419        this = self.sql(expression, "this")
2420        update = " UPDATE" if expression.args.get("update") else ""
2421        scope = self.sql(expression, "scope")
2422        scope = f" FOR {scope}" if scope else ""
2423
2424        return f"QUERY_BAND = {this}{update}{scope}"
def pragma_sql(self, expression: sqlglot.expressions.Pragma) -> str:
2426    def pragma_sql(self, expression: exp.Pragma) -> str:
2427        return f"PRAGMA {self.sql(expression, 'this')}"
def lock_sql(self, expression: sqlglot.expressions.Lock) -> str:
2429    def lock_sql(self, expression: exp.Lock) -> str:
2430        if not self.LOCKING_READS_SUPPORTED:
2431            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
2432            return ""
2433
2434        update = expression.args["update"]
2435        key = expression.args.get("key")
2436        if update:
2437            lock_type = "FOR NO KEY UPDATE" if key else "FOR UPDATE"
2438        else:
2439            lock_type = "FOR KEY SHARE" if key else "FOR SHARE"
2440        expressions = self.expressions(expression, flat=True)
2441        expressions = f" OF {expressions}" if expressions else ""
2442        wait = expression.args.get("wait")
2443
2444        if wait is not None:
2445            if isinstance(wait, exp.Literal):
2446                wait = f" WAIT {self.sql(wait)}"
2447            else:
2448                wait = " NOWAIT" if wait else " SKIP LOCKED"
2449
2450        return f"{lock_type}{expressions}{wait or ''}"
def literal_sql(self, expression: sqlglot.expressions.Literal) -> str:
2452    def literal_sql(self, expression: exp.Literal) -> str:
2453        text = expression.this or ""
2454        if expression.is_string:
2455            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
2456        return text
def escape_str(self, text: str, escape_backslash: bool = True) -> str:
2458    def escape_str(self, text: str, escape_backslash: bool = True) -> str:
2459        if self.dialect.ESCAPED_SEQUENCES:
2460            to_escaped = self.dialect.ESCAPED_SEQUENCES
2461            text = "".join(
2462                to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text
2463            )
2464
2465        return self._replace_line_breaks(text).replace(
2466            self.dialect.QUOTE_END, self._escaped_quote_end
2467        )
def loaddata_sql(self, expression: sqlglot.expressions.LoadData) -> str:
2469    def loaddata_sql(self, expression: exp.LoadData) -> str:
2470        local = " LOCAL" if expression.args.get("local") else ""
2471        inpath = f" INPATH {self.sql(expression, 'inpath')}"
2472        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
2473        this = f" INTO TABLE {self.sql(expression, 'this')}"
2474        partition = self.sql(expression, "partition")
2475        partition = f" {partition}" if partition else ""
2476        input_format = self.sql(expression, "input_format")
2477        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
2478        serde = self.sql(expression, "serde")
2479        serde = f" SERDE {serde}" if serde else ""
2480        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
def null_sql(self, *_) -> str:
2482    def null_sql(self, *_) -> str:
2483        return "NULL"
def boolean_sql(self, expression: sqlglot.expressions.Boolean) -> str:
2485    def boolean_sql(self, expression: exp.Boolean) -> str:
2486        return "TRUE" if expression.this else "FALSE"
def order_sql(self, expression: sqlglot.expressions.Order, flat: bool = False) -> str:
2488    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
2489        this = self.sql(expression, "this")
2490        this = f"{this} " if this else this
2491        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
2492        return self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
def withfill_sql(self, expression: sqlglot.expressions.WithFill) -> str:
2494    def withfill_sql(self, expression: exp.WithFill) -> str:
2495        from_sql = self.sql(expression, "from")
2496        from_sql = f" FROM {from_sql}" if from_sql else ""
2497        to_sql = self.sql(expression, "to")
2498        to_sql = f" TO {to_sql}" if to_sql else ""
2499        step_sql = self.sql(expression, "step")
2500        step_sql = f" STEP {step_sql}" if step_sql else ""
2501        interpolated_values = [
2502            f"{self.sql(e, 'alias')} AS {self.sql(e, 'this')}"
2503            if isinstance(e, exp.Alias)
2504            else self.sql(e, "this")
2505            for e in expression.args.get("interpolate") or []
2506        ]
2507        interpolate = (
2508            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
2509        )
2510        return f"WITH FILL{from_sql}{to_sql}{step_sql}{interpolate}"
def cluster_sql(self, expression: sqlglot.expressions.Cluster) -> str:
2512    def cluster_sql(self, expression: exp.Cluster) -> str:
2513        return self.op_expressions("CLUSTER BY", expression)
def distribute_sql(self, expression: sqlglot.expressions.Distribute) -> str:
2515    def distribute_sql(self, expression: exp.Distribute) -> str:
2516        return self.op_expressions("DISTRIBUTE BY", expression)
def sort_sql(self, expression: sqlglot.expressions.Sort) -> str:
2518    def sort_sql(self, expression: exp.Sort) -> str:
2519        return self.op_expressions("SORT BY", expression)
def ordered_sql(self, expression: sqlglot.expressions.Ordered) -> str:
2521    def ordered_sql(self, expression: exp.Ordered) -> str:
2522        desc = expression.args.get("desc")
2523        asc = not desc
2524
2525        nulls_first = expression.args.get("nulls_first")
2526        nulls_last = not nulls_first
2527        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
2528        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
2529        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
2530
2531        this = self.sql(expression, "this")
2532
2533        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
2534        nulls_sort_change = ""
2535        if nulls_first and (
2536            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
2537        ):
2538            nulls_sort_change = " NULLS FIRST"
2539        elif (
2540            nulls_last
2541            and ((asc and nulls_are_small) or (desc and nulls_are_large))
2542            and not nulls_are_last
2543        ):
2544            nulls_sort_change = " NULLS LAST"
2545
2546        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
2547        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
2548            window = expression.find_ancestor(exp.Window, exp.Select)
2549            if isinstance(window, exp.Window) and window.args.get("spec"):
2550                self.unsupported(
2551                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
2552                )
2553                nulls_sort_change = ""
2554            elif self.NULL_ORDERING_SUPPORTED is False and (
2555                (asc and nulls_sort_change == " NULLS LAST")
2556                or (desc and nulls_sort_change == " NULLS FIRST")
2557            ):
2558                # BigQuery does not allow these ordering/nulls combinations when used under
2559                # an aggregation func or under a window containing one
2560                ancestor = expression.find_ancestor(exp.AggFunc, exp.Window, exp.Select)
2561
2562                if isinstance(ancestor, exp.Window):
2563                    ancestor = ancestor.this
2564                if isinstance(ancestor, exp.AggFunc):
2565                    self.unsupported(
2566                        f"'{nulls_sort_change.strip()}' translation not supported for aggregate functions with {sort_order} sort order"
2567                    )
2568                    nulls_sort_change = ""
2569            elif self.NULL_ORDERING_SUPPORTED is None:
2570                if expression.this.is_int:
2571                    self.unsupported(
2572                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
2573                    )
2574                elif not isinstance(expression.this, exp.Rand):
2575                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
2576                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
2577                nulls_sort_change = ""
2578
2579        with_fill = self.sql(expression, "with_fill")
2580        with_fill = f" {with_fill}" if with_fill else ""
2581
2582        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
def matchrecognizemeasure_sql(self, expression: sqlglot.expressions.MatchRecognizeMeasure) -> str:
2584    def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str:
2585        window_frame = self.sql(expression, "window_frame")
2586        window_frame = f"{window_frame} " if window_frame else ""
2587
2588        this = self.sql(expression, "this")
2589
2590        return f"{window_frame}{this}"
def matchrecognize_sql(self, expression: sqlglot.expressions.MatchRecognize) -> str:
2592    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
2593        partition = self.partition_by_sql(expression)
2594        order = self.sql(expression, "order")
2595        measures = self.expressions(expression, key="measures")
2596        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
2597        rows = self.sql(expression, "rows")
2598        rows = self.seg(rows) if rows else ""
2599        after = self.sql(expression, "after")
2600        after = self.seg(after) if after else ""
2601        pattern = self.sql(expression, "pattern")
2602        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
2603        definition_sqls = [
2604            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
2605            for definition in expression.args.get("define", [])
2606        ]
2607        definitions = self.expressions(sqls=definition_sqls)
2608        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
2609        body = "".join(
2610            (
2611                partition,
2612                order,
2613                measures,
2614                rows,
2615                after,
2616                pattern,
2617                define,
2618            )
2619        )
2620        alias = self.sql(expression, "alias")
2621        alias = f" {alias}" if alias else ""
2622        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
def query_modifiers(self, expression: sqlglot.expressions.Expression, *sqls: str) -> str:
2624    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
2625        limit = expression.args.get("limit")
2626
2627        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
2628            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
2629        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
2630            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
2631
2632        return csv(
2633            *sqls,
2634            *[self.sql(join) for join in expression.args.get("joins") or []],
2635            self.sql(expression, "match"),
2636            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
2637            self.sql(expression, "prewhere"),
2638            self.sql(expression, "where"),
2639            self.sql(expression, "connect"),
2640            self.sql(expression, "group"),
2641            self.sql(expression, "having"),
2642            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
2643            self.sql(expression, "order"),
2644            *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
2645            *self.after_limit_modifiers(expression),
2646            self.options_modifier(expression),
2647            self.for_modifiers(expression),
2648            sep="",
2649        )
def options_modifier(self, expression: sqlglot.expressions.Expression) -> str:
2651    def options_modifier(self, expression: exp.Expression) -> str:
2652        options = self.expressions(expression, key="options")
2653        return f" {options}" if options else ""
def for_modifiers(self, expression: sqlglot.expressions.Expression) -> str:
2655    def for_modifiers(self, expression: exp.Expression) -> str:
2656        for_modifiers = self.expressions(expression, key="for")
2657        return f"{self.sep()}FOR XML{self.seg(for_modifiers)}" if for_modifiers else ""
def queryoption_sql(self, expression: sqlglot.expressions.QueryOption) -> str:
2659    def queryoption_sql(self, expression: exp.QueryOption) -> str:
2660        self.unsupported("Unsupported query option.")
2661        return ""
def offset_limit_modifiers( self, expression: sqlglot.expressions.Expression, fetch: bool, limit: Union[sqlglot.expressions.Fetch, sqlglot.expressions.Limit, NoneType]) -> List[str]:
2663    def offset_limit_modifiers(
2664        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
2665    ) -> t.List[str]:
2666        return [
2667            self.sql(expression, "offset") if fetch else self.sql(limit),
2668            self.sql(limit) if fetch else self.sql(expression, "offset"),
2669        ]
def after_limit_modifiers(self, expression: sqlglot.expressions.Expression) -> List[str]:
2671    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
2672        locks = self.expressions(expression, key="locks", sep=" ")
2673        locks = f" {locks}" if locks else ""
2674        return [locks, self.sql(expression, "sample")]
def select_sql(self, expression: sqlglot.expressions.Select) -> str:
2676    def select_sql(self, expression: exp.Select) -> str:
2677        into = expression.args.get("into")
2678        if not self.SUPPORTS_SELECT_INTO and into:
2679            into.pop()
2680
2681        hint = self.sql(expression, "hint")
2682        distinct = self.sql(expression, "distinct")
2683        distinct = f" {distinct}" if distinct else ""
2684        kind = self.sql(expression, "kind")
2685
2686        limit = expression.args.get("limit")
2687        if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP:
2688            top = self.limit_sql(limit, top=True)
2689            limit.pop()
2690        else:
2691            top = ""
2692
2693        expressions = self.expressions(expression)
2694
2695        if kind:
2696            if kind in self.SELECT_KINDS:
2697                kind = f" AS {kind}"
2698            else:
2699                if kind == "STRUCT":
2700                    expressions = self.expressions(
2701                        sqls=[
2702                            self.sql(
2703                                exp.Struct(
2704                                    expressions=[
2705                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
2706                                        if isinstance(e, exp.Alias)
2707                                        else e
2708                                        for e in expression.expressions
2709                                    ]
2710                                )
2711                            )
2712                        ]
2713                    )
2714                kind = ""
2715
2716        operation_modifiers = self.expressions(expression, key="operation_modifiers", sep=" ")
2717        operation_modifiers = f"{self.sep()}{operation_modifiers}" if operation_modifiers else ""
2718
2719        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
2720        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
2721        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
2722        expressions = f"{self.sep()}{expressions}" if expressions else expressions
2723        sql = self.query_modifiers(
2724            expression,
2725            f"SELECT{top_distinct}{operation_modifiers}{kind}{expressions}",
2726            self.sql(expression, "into", comment=False),
2727            self.sql(expression, "from", comment=False),
2728        )
2729
2730        # If both the CTE and SELECT clauses have comments, generate the latter earlier
2731        if expression.args.get("with"):
2732            sql = self.maybe_comment(sql, expression)
2733            expression.pop_comments()
2734
2735        sql = self.prepend_ctes(expression, sql)
2736
2737        if not self.SUPPORTS_SELECT_INTO and into:
2738            if into.args.get("temporary"):
2739                table_kind = " TEMPORARY"
2740            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
2741                table_kind = " UNLOGGED"
2742            else:
2743                table_kind = ""
2744            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
2745
2746        return sql
def schema_sql(self, expression: sqlglot.expressions.Schema) -> str:
2748    def schema_sql(self, expression: exp.Schema) -> str:
2749        this = self.sql(expression, "this")
2750        sql = self.schema_columns_sql(expression)
2751        return f"{this} {sql}" if this and sql else this or sql
def schema_columns_sql(self, expression: sqlglot.expressions.Schema) -> str:
2753    def schema_columns_sql(self, expression: exp.Schema) -> str:
2754        if expression.expressions:
2755            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
2756        return ""
def star_sql(self, expression: sqlglot.expressions.Star) -> str:
2758    def star_sql(self, expression: exp.Star) -> str:
2759        except_ = self.expressions(expression, key="except", flat=True)
2760        except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
2761        replace = self.expressions(expression, key="replace", flat=True)
2762        replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
2763        rename = self.expressions(expression, key="rename", flat=True)
2764        rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
2765        return f"*{except_}{replace}{rename}"
def parameter_sql(self, expression: sqlglot.expressions.Parameter) -> str:
2767    def parameter_sql(self, expression: exp.Parameter) -> str:
2768        this = self.sql(expression, "this")
2769        return f"{self.PARAMETER_TOKEN}{this}"
def sessionparameter_sql(self, expression: sqlglot.expressions.SessionParameter) -> str:
2771    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
2772        this = self.sql(expression, "this")
2773        kind = expression.text("kind")
2774        if kind:
2775            kind = f"{kind}."
2776        return f"@@{kind}{this}"
def placeholder_sql(self, expression: sqlglot.expressions.Placeholder) -> str:
2778    def placeholder_sql(self, expression: exp.Placeholder) -> str:
2779        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?"
def subquery_sql(self, expression: sqlglot.expressions.Subquery, sep: str = ' AS ') -> str:
2781    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
2782        alias = self.sql(expression, "alias")
2783        alias = f"{sep}{alias}" if alias else ""
2784        sample = self.sql(expression, "sample")
2785        if self.dialect.ALIAS_POST_TABLESAMPLE and sample:
2786            alias = f"{sample}{alias}"
2787
2788            # Set to None so it's not generated again by self.query_modifiers()
2789            expression.set("sample", None)
2790
2791        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2792        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
2793        return self.prepend_ctes(expression, sql)
def qualify_sql(self, expression: sqlglot.expressions.Qualify) -> str:
2795    def qualify_sql(self, expression: exp.Qualify) -> str:
2796        this = self.indent(self.sql(expression, "this"))
2797        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
def unnest_sql(self, expression: sqlglot.expressions.Unnest) -> str:
2799    def unnest_sql(self, expression: exp.Unnest) -> str:
2800        args = self.expressions(expression, flat=True)
2801
2802        alias = expression.args.get("alias")
2803        offset = expression.args.get("offset")
2804
2805        if self.UNNEST_WITH_ORDINALITY:
2806            if alias and isinstance(offset, exp.Expression):
2807                alias.append("columns", offset)
2808
2809        if alias and self.dialect.UNNEST_COLUMN_ONLY:
2810            columns = alias.columns
2811            alias = self.sql(columns[0]) if columns else ""
2812        else:
2813            alias = self.sql(alias)
2814
2815        alias = f" AS {alias}" if alias else alias
2816        if self.UNNEST_WITH_ORDINALITY:
2817            suffix = f" WITH ORDINALITY{alias}" if offset else alias
2818        else:
2819            if isinstance(offset, exp.Expression):
2820                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
2821            elif offset:
2822                suffix = f"{alias} WITH OFFSET"
2823            else:
2824                suffix = alias
2825
2826        return f"UNNEST({args}){suffix}"
def prewhere_sql(self, expression: sqlglot.expressions.PreWhere) -> str:
2828    def prewhere_sql(self, expression: exp.PreWhere) -> str:
2829        return ""
def where_sql(self, expression: sqlglot.expressions.Where) -> str:
2831    def where_sql(self, expression: exp.Where) -> str:
2832        this = self.indent(self.sql(expression, "this"))
2833        return f"{self.seg('WHERE')}{self.sep()}{this}"
def window_sql(self, expression: sqlglot.expressions.Window) -> str:
2835    def window_sql(self, expression: exp.Window) -> str:
2836        this = self.sql(expression, "this")
2837        partition = self.partition_by_sql(expression)
2838        order = expression.args.get("order")
2839        order = self.order_sql(order, flat=True) if order else ""
2840        spec = self.sql(expression, "spec")
2841        alias = self.sql(expression, "alias")
2842        over = self.sql(expression, "over") or "OVER"
2843
2844        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
2845
2846        first = expression.args.get("first")
2847        if first is None:
2848            first = ""
2849        else:
2850            first = "FIRST" if first else "LAST"
2851
2852        if not partition and not order and not spec and alias:
2853            return f"{this} {alias}"
2854
2855        args = self.format_args(
2856            *[arg for arg in (alias, first, partition, order, spec) if arg], sep=" "
2857        )
2858        return f"{this} ({args})"
def partition_by_sql( self, expression: sqlglot.expressions.Window | sqlglot.expressions.MatchRecognize) -> str:
2860    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
2861        partition = self.expressions(expression, key="partition_by", flat=True)
2862        return f"PARTITION BY {partition}" if partition else ""
def windowspec_sql(self, expression: sqlglot.expressions.WindowSpec) -> str:
2864    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
2865        kind = self.sql(expression, "kind")
2866        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
2867        end = (
2868            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
2869            or "CURRENT ROW"
2870        )
2871
2872        window_spec = f"{kind} BETWEEN {start} AND {end}"
2873
2874        exclude = self.sql(expression, "exclude")
2875        if exclude:
2876            if self.SUPPORTS_WINDOW_EXCLUDE:
2877                window_spec += f" EXCLUDE {exclude}"
2878            else:
2879                self.unsupported("EXCLUDE clause is not supported in the WINDOW clause")
2880
2881        return window_spec
def withingroup_sql(self, expression: sqlglot.expressions.WithinGroup) -> str:
2883    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
2884        this = self.sql(expression, "this")
2885        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
2886        return f"{this} WITHIN GROUP ({expression_sql})"
def between_sql(self, expression: sqlglot.expressions.Between) -> str:
2888    def between_sql(self, expression: exp.Between) -> str:
2889        this = self.sql(expression, "this")
2890        low = self.sql(expression, "low")
2891        high = self.sql(expression, "high")
2892        symmetric = expression.args.get("symmetric")
2893
2894        if symmetric and not self.SUPPORTS_BETWEEN_FLAGS:
2895            return f"({this} BETWEEN {low} AND {high} OR {this} BETWEEN {high} AND {low})"
2896
2897        flag = (
2898            " SYMMETRIC"
2899            if symmetric
2900            else " ASYMMETRIC"
2901            if symmetric is False and self.SUPPORTS_BETWEEN_FLAGS
2902            else ""  # silently drop ASYMMETRIC – semantics identical
2903        )
2904        return f"{this} BETWEEN{flag} {low} AND {high}"
def bracket_offset_expressions( self, expression: sqlglot.expressions.Bracket, index_offset: Optional[int] = None) -> List[sqlglot.expressions.Expression]:
2906    def bracket_offset_expressions(
2907        self, expression: exp.Bracket, index_offset: t.Optional[int] = None
2908    ) -> t.List[exp.Expression]:
2909        return apply_index_offset(
2910            expression.this,
2911            expression.expressions,
2912            (index_offset or self.dialect.INDEX_OFFSET) - expression.args.get("offset", 0),
2913            dialect=self.dialect,
2914        )
def bracket_sql(self, expression: sqlglot.expressions.Bracket) -> str:
2916    def bracket_sql(self, expression: exp.Bracket) -> str:
2917        expressions = self.bracket_offset_expressions(expression)
2918        expressions_sql = ", ".join(self.sql(e) for e in expressions)
2919        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
def all_sql(self, expression: sqlglot.expressions.All) -> str:
2921    def all_sql(self, expression: exp.All) -> str:
2922        this = self.sql(expression, "this")
2923        if not isinstance(expression.this, (exp.Tuple, exp.Paren)):
2924            this = self.wrap(this)
2925        return f"ALL {this}"
def any_sql(self, expression: sqlglot.expressions.Any) -> str:
2927    def any_sql(self, expression: exp.Any) -> str:
2928        this = self.sql(expression, "this")
2929        if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)):
2930            if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
2931                this = self.wrap(this)
2932            return f"ANY{this}"
2933        return f"ANY {this}"
def exists_sql(self, expression: sqlglot.expressions.Exists) -> str:
2935    def exists_sql(self, expression: exp.Exists) -> str:
2936        return f"EXISTS{self.wrap(expression)}"
def case_sql(self, expression: sqlglot.expressions.Case) -> str:
2938    def case_sql(self, expression: exp.Case) -> str:
2939        this = self.sql(expression, "this")
2940        statements = [f"CASE {this}" if this else "CASE"]
2941
2942        for e in expression.args["ifs"]:
2943            statements.append(f"WHEN {self.sql(e, 'this')}")
2944            statements.append(f"THEN {self.sql(e, 'true')}")
2945
2946        default = self.sql(expression, "default")
2947
2948        if default:
2949            statements.append(f"ELSE {default}")
2950
2951        statements.append("END")
2952
2953        if self.pretty and self.too_wide(statements):
2954            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
2955
2956        return " ".join(statements)
def constraint_sql(self, expression: sqlglot.expressions.Constraint) -> str:
2958    def constraint_sql(self, expression: exp.Constraint) -> str:
2959        this = self.sql(expression, "this")
2960        expressions = self.expressions(expression, flat=True)
2961        return f"CONSTRAINT {this} {expressions}"
def nextvaluefor_sql(self, expression: sqlglot.expressions.NextValueFor) -> str:
2963    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
2964        order = expression.args.get("order")
2965        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
2966        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
def extract_sql(self, expression: sqlglot.expressions.Extract) -> str:
2968    def extract_sql(self, expression: exp.Extract) -> str:
2969        from sqlglot.dialects.dialect import map_date_part
2970
2971        this = (
2972            map_date_part(expression.this, self.dialect)
2973            if self.NORMALIZE_EXTRACT_DATE_PARTS
2974            else expression.this
2975        )
2976        this_sql = self.sql(this) if self.EXTRACT_ALLOWS_QUOTES else this.name
2977        expression_sql = self.sql(expression, "expression")
2978
2979        return f"EXTRACT({this_sql} FROM {expression_sql})"
def trim_sql(self, expression: sqlglot.expressions.Trim) -> str:
2981    def trim_sql(self, expression: exp.Trim) -> str:
2982        trim_type = self.sql(expression, "position")
2983
2984        if trim_type == "LEADING":
2985            func_name = "LTRIM"
2986        elif trim_type == "TRAILING":
2987            func_name = "RTRIM"
2988        else:
2989            func_name = "TRIM"
2990
2991        return self.func(func_name, expression.this, expression.expression)
def convert_concat_args( self, expression: sqlglot.expressions.Concat | sqlglot.expressions.ConcatWs) -> List[sqlglot.expressions.Expression]:
2993    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
2994        args = expression.expressions
2995        if isinstance(expression, exp.ConcatWs):
2996            args = args[1:]  # Skip the delimiter
2997
2998        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2999            args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args]
3000
3001        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
3002
3003            def _wrap_with_coalesce(e: exp.Expression) -> exp.Expression:
3004                if not e.type:
3005                    from sqlglot.optimizer.annotate_types import annotate_types
3006
3007                    e = annotate_types(e, dialect=self.dialect)
3008
3009                if e.is_string or e.is_type(exp.DataType.Type.ARRAY):
3010                    return e
3011
3012                return exp.func("coalesce", e, exp.Literal.string(""))
3013
3014            args = [_wrap_with_coalesce(e) for e in args]
3015
3016        return args
def concat_sql(self, expression: sqlglot.expressions.Concat) -> str:
3018    def concat_sql(self, expression: exp.Concat) -> str:
3019        expressions = self.convert_concat_args(expression)
3020
3021        # Some dialects don't allow a single-argument CONCAT call
3022        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
3023            return self.sql(expressions[0])
3024
3025        return self.func("CONCAT", *expressions)
def concatws_sql(self, expression: sqlglot.expressions.ConcatWs) -> str:
3027    def concatws_sql(self, expression: exp.ConcatWs) -> str:
3028        return self.func(
3029            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
3030        )
def check_sql(self, expression: sqlglot.expressions.Check) -> str:
3032    def check_sql(self, expression: exp.Check) -> str:
3033        this = self.sql(expression, key="this")
3034        return f"CHECK ({this})"
def foreignkey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str:
3036    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
3037        expressions = self.expressions(expression, flat=True)
3038        expressions = f" ({expressions})" if expressions else ""
3039        reference = self.sql(expression, "reference")
3040        reference = f" {reference}" if reference else ""
3041        delete = self.sql(expression, "delete")
3042        delete = f" ON DELETE {delete}" if delete else ""
3043        update = self.sql(expression, "update")
3044        update = f" ON UPDATE {update}" if update else ""
3045        options = self.expressions(expression, key="options", flat=True, sep=" ")
3046        options = f" {options}" if options else ""
3047        return f"FOREIGN KEY{expressions}{reference}{delete}{update}{options}"
def primarykey_sql(self, expression: sqlglot.expressions.PrimaryKey) -> str:
3049    def primarykey_sql(self, expression: exp.PrimaryKey) -> str:
3050        expressions = self.expressions(expression, flat=True)
3051        include = self.sql(expression, "include")
3052        options = self.expressions(expression, key="options", flat=True, sep=" ")
3053        options = f" {options}" if options else ""
3054        return f"PRIMARY KEY ({expressions}){include}{options}"
def if_sql(self, expression: sqlglot.expressions.If) -> str:
3056    def if_sql(self, expression: exp.If) -> str:
3057        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
def matchagainst_sql(self, expression: sqlglot.expressions.MatchAgainst) -> str:
3059    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
3060        modifier = expression.args.get("modifier")
3061        modifier = f" {modifier}" if modifier else ""
3062        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
def jsonkeyvalue_sql(self, expression: sqlglot.expressions.JSONKeyValue) -> str:
3064    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
3065        return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"
def jsonpath_sql(self, expression: sqlglot.expressions.JSONPath) -> str:
3067    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
3068        path = self.expressions(expression, sep="", flat=True).lstrip(".")
3069
3070        if expression.args.get("escape"):
3071            path = self.escape_str(path)
3072
3073        if self.QUOTE_JSON_PATH:
3074            path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
3075
3076        return path
def json_path_part(self, expression: int | str | sqlglot.expressions.JSONPathPart) -> str:
3078    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
3079        if isinstance(expression, exp.JSONPathPart):
3080            transform = self.TRANSFORMS.get(expression.__class__)
3081            if not callable(transform):
3082                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
3083                return ""
3084
3085            return transform(self, expression)
3086
3087        if isinstance(expression, int):
3088            return str(expression)
3089
3090        if self._quote_json_path_key_using_brackets and self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
3091            escaped = expression.replace("'", "\\'")
3092            escaped = f"\\'{expression}\\'"
3093        else:
3094            escaped = expression.replace('"', '\\"')
3095            escaped = f'"{escaped}"'
3096
3097        return escaped
def formatjson_sql(self, expression: sqlglot.expressions.FormatJson) -> str:
3099    def formatjson_sql(self, expression: exp.FormatJson) -> str:
3100        return f"{self.sql(expression, 'this')} FORMAT JSON"
def formatphrase_sql(self, expression: sqlglot.expressions.FormatPhrase) -> str:
3102    def formatphrase_sql(self, expression: exp.FormatPhrase) -> str:
3103        # Output the Teradata column FORMAT override.
3104        # https://docs.teradata.com/r/Enterprise_IntelliFlex_VMware/SQL-Data-Types-and-Literals/Data-Type-Formats-and-Format-Phrases/FORMAT
3105        this = self.sql(expression, "this")
3106        fmt = self.sql(expression, "format")
3107        return f"{this} (FORMAT {fmt})"
def jsonobject_sql( self, expression: sqlglot.expressions.JSONObject | sqlglot.expressions.JSONObjectAgg) -> str:
3109    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
3110        null_handling = expression.args.get("null_handling")
3111        null_handling = f" {null_handling}" if null_handling else ""
3112
3113        unique_keys = expression.args.get("unique_keys")
3114        if unique_keys is not None:
3115            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
3116        else:
3117            unique_keys = ""
3118
3119        return_type = self.sql(expression, "return_type")
3120        return_type = f" RETURNING {return_type}" if return_type else ""
3121        encoding = self.sql(expression, "encoding")
3122        encoding = f" ENCODING {encoding}" if encoding else ""
3123
3124        return self.func(
3125            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
3126            *expression.expressions,
3127            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
3128        )
def jsonobjectagg_sql(self, expression: sqlglot.expressions.JSONObjectAgg) -> str:
3130    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
3131        return self.jsonobject_sql(expression)
def jsonarray_sql(self, expression: sqlglot.expressions.JSONArray) -> str:
3133    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
3134        null_handling = expression.args.get("null_handling")
3135        null_handling = f" {null_handling}" if null_handling else ""
3136        return_type = self.sql(expression, "return_type")
3137        return_type = f" RETURNING {return_type}" if return_type else ""
3138        strict = " STRICT" if expression.args.get("strict") else ""
3139        return self.func(
3140            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
3141        )
def jsonarrayagg_sql(self, expression: sqlglot.expressions.JSONArrayAgg) -> str:
3143    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
3144        this = self.sql(expression, "this")
3145        order = self.sql(expression, "order")
3146        null_handling = expression.args.get("null_handling")
3147        null_handling = f" {null_handling}" if null_handling else ""
3148        return_type = self.sql(expression, "return_type")
3149        return_type = f" RETURNING {return_type}" if return_type else ""
3150        strict = " STRICT" if expression.args.get("strict") else ""
3151        return self.func(
3152            "JSON_ARRAYAGG",
3153            this,
3154            suffix=f"{order}{null_handling}{return_type}{strict})",
3155        )
def jsoncolumndef_sql(self, expression: sqlglot.expressions.JSONColumnDef) -> str:
3157    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
3158        path = self.sql(expression, "path")
3159        path = f" PATH {path}" if path else ""
3160        nested_schema = self.sql(expression, "nested_schema")
3161
3162        if nested_schema:
3163            return f"NESTED{path} {nested_schema}"
3164
3165        this = self.sql(expression, "this")
3166        kind = self.sql(expression, "kind")
3167        kind = f" {kind}" if kind else ""
3168        return f"{this}{kind}{path}"
def jsonschema_sql(self, expression: sqlglot.expressions.JSONSchema) -> str:
3170    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
3171        return self.func("COLUMNS", *expression.expressions)
def jsontable_sql(self, expression: sqlglot.expressions.JSONTable) -> str:
3173    def jsontable_sql(self, expression: exp.JSONTable) -> str:
3174        this = self.sql(expression, "this")
3175        path = self.sql(expression, "path")
3176        path = f", {path}" if path else ""
3177        error_handling = expression.args.get("error_handling")
3178        error_handling = f" {error_handling}" if error_handling else ""
3179        empty_handling = expression.args.get("empty_handling")
3180        empty_handling = f" {empty_handling}" if empty_handling else ""
3181        schema = self.sql(expression, "schema")
3182        return self.func(
3183            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
3184        )
def openjsoncolumndef_sql(self, expression: sqlglot.expressions.OpenJSONColumnDef) -> str:
3186    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
3187        this = self.sql(expression, "this")
3188        kind = self.sql(expression, "kind")
3189        path = self.sql(expression, "path")
3190        path = f" {path}" if path else ""
3191        as_json = " AS JSON" if expression.args.get("as_json") else ""
3192        return f"{this} {kind}{path}{as_json}"
def openjson_sql(self, expression: sqlglot.expressions.OpenJSON) -> str:
3194    def openjson_sql(self, expression: exp.OpenJSON) -> str:
3195        this = self.sql(expression, "this")
3196        path = self.sql(expression, "path")
3197        path = f", {path}" if path else ""
3198        expressions = self.expressions(expression)
3199        with_ = (
3200            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
3201            if expressions
3202            else ""
3203        )
3204        return f"OPENJSON({this}{path}){with_}"
def in_sql(self, expression: sqlglot.expressions.In) -> str:
3206    def in_sql(self, expression: exp.In) -> str:
3207        query = expression.args.get("query")
3208        unnest = expression.args.get("unnest")
3209        field = expression.args.get("field")
3210        is_global = " GLOBAL" if expression.args.get("is_global") else ""
3211
3212        if query:
3213            in_sql = self.sql(query)
3214        elif unnest:
3215            in_sql = self.in_unnest_op(unnest)
3216        elif field:
3217            in_sql = self.sql(field)
3218        else:
3219            in_sql = f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
3220
3221        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
def in_unnest_op(self, unnest: sqlglot.expressions.Unnest) -> str:
3223    def in_unnest_op(self, unnest: exp.Unnest) -> str:
3224        return f"(SELECT {self.sql(unnest)})"
def interval_sql(self, expression: sqlglot.expressions.Interval) -> str:
3226    def interval_sql(self, expression: exp.Interval) -> str:
3227        unit = self.sql(expression, "unit")
3228        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
3229            unit = self.TIME_PART_SINGULARS.get(unit, unit)
3230        unit = f" {unit}" if unit else ""
3231
3232        if self.SINGLE_STRING_INTERVAL:
3233            this = expression.this.name if expression.this else ""
3234            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
3235
3236        this = self.sql(expression, "this")
3237        if this:
3238            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
3239            this = f" {this}" if unwrapped else f" ({this})"
3240
3241        return f"INTERVAL{this}{unit}"
def return_sql(self, expression: sqlglot.expressions.Return) -> str:
3243    def return_sql(self, expression: exp.Return) -> str:
3244        return f"RETURN {self.sql(expression, 'this')}"
def reference_sql(self, expression: sqlglot.expressions.Reference) -> str:
3246    def reference_sql(self, expression: exp.Reference) -> str:
3247        this = self.sql(expression, "this")
3248        expressions = self.expressions(expression, flat=True)
3249        expressions = f"({expressions})" if expressions else ""
3250        options = self.expressions(expression, key="options", flat=True, sep=" ")
3251        options = f" {options}" if options else ""
3252        return f"REFERENCES {this}{expressions}{options}"
def anonymous_sql(self, expression: sqlglot.expressions.Anonymous) -> str:
3254    def anonymous_sql(self, expression: exp.Anonymous) -> str:
3255        # We don't normalize qualified functions such as a.b.foo(), because they can be case-sensitive
3256        parent = expression.parent
3257        is_qualified = isinstance(parent, exp.Dot) and expression is parent.expression
3258        return self.func(
3259            self.sql(expression, "this"), *expression.expressions, normalize=not is_qualified
3260        )
def paren_sql(self, expression: sqlglot.expressions.Paren) -> str:
3262    def paren_sql(self, expression: exp.Paren) -> str:
3263        sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
3264        return f"({sql}{self.seg(')', sep='')}"
def neg_sql(self, expression: sqlglot.expressions.Neg) -> str:
3266    def neg_sql(self, expression: exp.Neg) -> str:
3267        # This makes sure we don't convert "- - 5" to "--5", which is a comment
3268        this_sql = self.sql(expression, "this")
3269        sep = " " if this_sql[0] == "-" else ""
3270        return f"-{sep}{this_sql}"
def not_sql(self, expression: sqlglot.expressions.Not) -> str:
3272    def not_sql(self, expression: exp.Not) -> str:
3273        return f"NOT {self.sql(expression, 'this')}"
def alias_sql(self, expression: sqlglot.expressions.Alias) -> str:
3275    def alias_sql(self, expression: exp.Alias) -> str:
3276        alias = self.sql(expression, "alias")
3277        alias = f" AS {alias}" if alias else ""
3278        return f"{self.sql(expression, 'this')}{alias}"
def pivotalias_sql(self, expression: sqlglot.expressions.PivotAlias) -> str:
3280    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
3281        alias = expression.args["alias"]
3282
3283        parent = expression.parent
3284        pivot = parent and parent.parent
3285
3286        if isinstance(pivot, exp.Pivot) and pivot.unpivot:
3287            identifier_alias = isinstance(alias, exp.Identifier)
3288            literal_alias = isinstance(alias, exp.Literal)
3289
3290            if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
3291                alias.replace(exp.Literal.string(alias.output_name))
3292            elif not identifier_alias and literal_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
3293                alias.replace(exp.to_identifier(alias.output_name))
3294
3295        return self.alias_sql(expression)
def aliases_sql(self, expression: sqlglot.expressions.Aliases) -> str:
3297    def aliases_sql(self, expression: exp.Aliases) -> str:
3298        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
def atindex_sql(self, expression: sqlglot.expressions.AtTimeZone) -> str:
3300    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
3301        this = self.sql(expression, "this")
3302        index = self.sql(expression, "expression")
3303        return f"{this} AT {index}"
def attimezone_sql(self, expression: sqlglot.expressions.AtTimeZone) -> str:
3305    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
3306        this = self.sql(expression, "this")
3307        zone = self.sql(expression, "zone")
3308        return f"{this} AT TIME ZONE {zone}"
def fromtimezone_sql(self, expression: sqlglot.expressions.FromTimeZone) -> str:
3310    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
3311        this = self.sql(expression, "this")
3312        zone = self.sql(expression, "zone")
3313        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
def add_sql(self, expression: sqlglot.expressions.Add) -> str:
3315    def add_sql(self, expression: exp.Add) -> str:
3316        return self.binary(expression, "+")
def and_sql( self, expression: sqlglot.expressions.And, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str:
3318    def and_sql(
3319        self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None
3320    ) -> str:
3321        return self.connector_sql(expression, "AND", stack)
def or_sql( self, expression: sqlglot.expressions.Or, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str:
3323    def or_sql(
3324        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
3325    ) -> str:
3326        return self.connector_sql(expression, "OR", stack)
def xor_sql( self, expression: sqlglot.expressions.Xor, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str:
3328    def xor_sql(
3329        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
3330    ) -> str:
3331        return self.connector_sql(expression, "XOR", stack)
def connector_sql( self, expression: sqlglot.expressions.Connector, op: str, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str:
3333    def connector_sql(
3334        self,
3335        expression: exp.Connector,
3336        op: str,
3337        stack: t.Optional[t.List[str | exp.Expression]] = None,
3338    ) -> str:
3339        if stack is not None:
3340            if expression.expressions:
3341                stack.append(self.expressions(expression, sep=f" {op} "))
3342            else:
3343                stack.append(expression.right)
3344                if expression.comments and self.comments:
3345                    for comment in expression.comments:
3346                        if comment:
3347                            op += f" /*{self.sanitize_comment(comment)}*/"
3348                stack.extend((op, expression.left))
3349            return op
3350
3351        stack = [expression]
3352        sqls: t.List[str] = []
3353        ops = set()
3354
3355        while stack:
3356            node = stack.pop()
3357            if isinstance(node, exp.Connector):
3358                ops.add(getattr(self, f"{node.key}_sql")(node, stack))
3359            else:
3360                sql = self.sql(node)
3361                if sqls and sqls[-1] in ops:
3362                    sqls[-1] += f" {sql}"
3363                else:
3364                    sqls.append(sql)
3365
3366        sep = "\n" if self.pretty and self.too_wide(sqls) else " "
3367        return sep.join(sqls)
def bitwiseand_sql(self, expression: sqlglot.expressions.BitwiseAnd) -> str:
3369    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
3370        return self.binary(expression, "&")
def bitwiseleftshift_sql(self, expression: sqlglot.expressions.BitwiseLeftShift) -> str:
3372    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
3373        return self.binary(expression, "<<")
def bitwisenot_sql(self, expression: sqlglot.expressions.BitwiseNot) -> str:
3375    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
3376        return f"~{self.sql(expression, 'this')}"
def bitwiseor_sql(self, expression: sqlglot.expressions.BitwiseOr) -> str:
3378    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
3379        return self.binary(expression, "|")
def bitwiserightshift_sql(self, expression: sqlglot.expressions.BitwiseRightShift) -> str:
3381    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
3382        return self.binary(expression, ">>")
def bitwisexor_sql(self, expression: sqlglot.expressions.BitwiseXor) -> str:
3384    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
3385        return self.binary(expression, "^")
def cast_sql( self, expression: sqlglot.expressions.Cast, safe_prefix: Optional[str] = None) -> str:
3387    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
3388        format_sql = self.sql(expression, "format")
3389        format_sql = f" FORMAT {format_sql}" if format_sql else ""
3390        to_sql = self.sql(expression, "to")
3391        to_sql = f" {to_sql}" if to_sql else ""
3392        action = self.sql(expression, "action")
3393        action = f" {action}" if action else ""
3394        default = self.sql(expression, "default")
3395        default = f" DEFAULT {default} ON CONVERSION ERROR" if default else ""
3396        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{default}{format_sql}{action})"
def currentdate_sql(self, expression: sqlglot.expressions.CurrentDate) -> str:
3398    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
3399        zone = self.sql(expression, "this")
3400        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
def collate_sql(self, expression: sqlglot.expressions.Collate) -> str:
3402    def collate_sql(self, expression: exp.Collate) -> str:
3403        if self.COLLATE_IS_FUNC:
3404            return self.function_fallback_sql(expression)
3405        return self.binary(expression, "COLLATE")
def command_sql(self, expression: sqlglot.expressions.Command) -> str:
3407    def command_sql(self, expression: exp.Command) -> str:
3408        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
def comment_sql(self, expression: sqlglot.expressions.Comment) -> str:
3410    def comment_sql(self, expression: exp.Comment) -> str:
3411        this = self.sql(expression, "this")
3412        kind = expression.args["kind"]
3413        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
3414        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
3415        expression_sql = self.sql(expression, "expression")
3416        return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"
def mergetreettlaction_sql(self, expression: sqlglot.expressions.MergeTreeTTLAction) -> str:
3418    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
3419        this = self.sql(expression, "this")
3420        delete = " DELETE" if expression.args.get("delete") else ""
3421        recompress = self.sql(expression, "recompress")
3422        recompress = f" RECOMPRESS {recompress}" if recompress else ""
3423        to_disk = self.sql(expression, "to_disk")
3424        to_disk = f" TO DISK {to_disk}" if to_disk else ""
3425        to_volume = self.sql(expression, "to_volume")
3426        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
3427        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
def mergetreettl_sql(self, expression: sqlglot.expressions.MergeTreeTTL) -> str:
3429    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
3430        where = self.sql(expression, "where")
3431        group = self.sql(expression, "group")
3432        aggregates = self.expressions(expression, key="aggregates")
3433        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
3434
3435        if not (where or group or aggregates) and len(expression.expressions) == 1:
3436            return f"TTL {self.expressions(expression, flat=True)}"
3437
3438        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
def transaction_sql(self, expression: sqlglot.expressions.Transaction) -> str:
3440    def transaction_sql(self, expression: exp.Transaction) -> str:
3441        return "BEGIN"
def commit_sql(self, expression: sqlglot.expressions.Commit) -> str:
3443    def commit_sql(self, expression: exp.Commit) -> str:
3444        chain = expression.args.get("chain")
3445        if chain is not None:
3446            chain = " AND CHAIN" if chain else " AND NO CHAIN"
3447
3448        return f"COMMIT{chain or ''}"
def rollback_sql(self, expression: sqlglot.expressions.Rollback) -> str:
3450    def rollback_sql(self, expression: exp.Rollback) -> str:
3451        savepoint = expression.args.get("savepoint")
3452        savepoint = f" TO {savepoint}" if savepoint else ""
3453        return f"ROLLBACK{savepoint}"
def altercolumn_sql(self, expression: sqlglot.expressions.AlterColumn) -> str:
3455    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
3456        this = self.sql(expression, "this")
3457
3458        dtype = self.sql(expression, "dtype")
3459        if dtype:
3460            collate = self.sql(expression, "collate")
3461            collate = f" COLLATE {collate}" if collate else ""
3462            using = self.sql(expression, "using")
3463            using = f" USING {using}" if using else ""
3464            alter_set_type = self.ALTER_SET_TYPE + " " if self.ALTER_SET_TYPE else ""
3465            return f"ALTER COLUMN {this} {alter_set_type}{dtype}{collate}{using}"
3466
3467        default = self.sql(expression, "default")
3468        if default:
3469            return f"ALTER COLUMN {this} SET DEFAULT {default}"
3470
3471        comment = self.sql(expression, "comment")
3472        if comment:
3473            return f"ALTER COLUMN {this} COMMENT {comment}"
3474
3475        visible = expression.args.get("visible")
3476        if visible:
3477            return f"ALTER COLUMN {this} SET {visible}"
3478
3479        allow_null = expression.args.get("allow_null")
3480        drop = expression.args.get("drop")
3481
3482        if not drop and not allow_null:
3483            self.unsupported("Unsupported ALTER COLUMN syntax")
3484
3485        if allow_null is not None:
3486            keyword = "DROP" if drop else "SET"
3487            return f"ALTER COLUMN {this} {keyword} NOT NULL"
3488
3489        return f"ALTER COLUMN {this} DROP DEFAULT"
def alterindex_sql(self, expression: sqlglot.expressions.AlterIndex) -> str:
3491    def alterindex_sql(self, expression: exp.AlterIndex) -> str:
3492        this = self.sql(expression, "this")
3493
3494        visible = expression.args.get("visible")
3495        visible_sql = "VISIBLE" if visible else "INVISIBLE"
3496
3497        return f"ALTER INDEX {this} {visible_sql}"
def alterdiststyle_sql(self, expression: sqlglot.expressions.AlterDistStyle) -> str:
3499    def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
3500        this = self.sql(expression, "this")
3501        if not isinstance(expression.this, exp.Var):
3502            this = f"KEY DISTKEY {this}"
3503        return f"ALTER DISTSTYLE {this}"
def altersortkey_sql(self, expression: sqlglot.expressions.AlterSortKey) -> str:
3505    def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
3506        compound = " COMPOUND" if expression.args.get("compound") else ""
3507        this = self.sql(expression, "this")
3508        expressions = self.expressions(expression, flat=True)
3509        expressions = f"({expressions})" if expressions else ""
3510        return f"ALTER{compound} SORTKEY {this or expressions}"
def alterrename_sql( self, expression: sqlglot.expressions.AlterRename, include_to: bool = True) -> str:
3512    def alterrename_sql(self, expression: exp.AlterRename, include_to: bool = True) -> str:
3513        if not self.RENAME_TABLE_WITH_DB:
3514            # Remove db from tables
3515            expression = expression.transform(
3516                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
3517            ).assert_is(exp.AlterRename)
3518        this = self.sql(expression, "this")
3519        to_kw = " TO" if include_to else ""
3520        return f"RENAME{to_kw} {this}"
def renamecolumn_sql(self, expression: sqlglot.expressions.RenameColumn) -> str:
3522    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
3523        exists = " IF EXISTS" if expression.args.get("exists") else ""
3524        old_column = self.sql(expression, "this")
3525        new_column = self.sql(expression, "to")
3526        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
def alterset_sql(self, expression: sqlglot.expressions.AlterSet) -> str:
3528    def alterset_sql(self, expression: exp.AlterSet) -> str:
3529        exprs = self.expressions(expression, flat=True)
3530        if self.ALTER_SET_WRAPPED:
3531            exprs = f"({exprs})"
3532
3533        return f"SET {exprs}"
def alter_sql(self, expression: sqlglot.expressions.Alter) -> str:
3535    def alter_sql(self, expression: exp.Alter) -> str:
3536        actions = expression.args["actions"]
3537
3538        if not self.dialect.ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN and isinstance(
3539            actions[0], exp.ColumnDef
3540        ):
3541            actions_sql = self.expressions(expression, key="actions", flat=True)
3542            actions_sql = f"ADD {actions_sql}"
3543        else:
3544            actions_list = []
3545            for action in actions:
3546                if isinstance(action, (exp.ColumnDef, exp.Schema)):
3547                    action_sql = self.add_column_sql(action)
3548                else:
3549                    action_sql = self.sql(action)
3550                    if isinstance(action, exp.Query):
3551                        action_sql = f"AS {action_sql}"
3552
3553                actions_list.append(action_sql)
3554
3555            actions_sql = self.format_args(*actions_list).lstrip("\n")
3556
3557        exists = " IF EXISTS" if expression.args.get("exists") else ""
3558        on_cluster = self.sql(expression, "cluster")
3559        on_cluster = f" {on_cluster}" if on_cluster else ""
3560        only = " ONLY" if expression.args.get("only") else ""
3561        options = self.expressions(expression, key="options")
3562        options = f", {options}" if options else ""
3563        kind = self.sql(expression, "kind")
3564        not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
3565        check = " WITH CHECK" if expression.args.get("check") else ""
3566
3567        return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster}{check}{self.sep()}{actions_sql}{not_valid}{options}"
def add_column_sql(self, expression: sqlglot.expressions.Expression) -> str:
3569    def add_column_sql(self, expression: exp.Expression) -> str:
3570        sql = self.sql(expression)
3571        if isinstance(expression, exp.Schema):
3572            column_text = " COLUMNS"
3573        elif isinstance(expression, exp.ColumnDef) and self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
3574            column_text = " COLUMN"
3575        else:
3576            column_text = ""
3577
3578        return f"ADD{column_text} {sql}"
def droppartition_sql(self, expression: sqlglot.expressions.DropPartition) -> str:
3580    def droppartition_sql(self, expression: exp.DropPartition) -> str:
3581        expressions = self.expressions(expression)
3582        exists = " IF EXISTS " if expression.args.get("exists") else " "
3583        return f"DROP{exists}{expressions}"
def addconstraint_sql(self, expression: sqlglot.expressions.AddConstraint) -> str:
3585    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
3586        return f"ADD {self.expressions(expression, indent=False)}"
def addpartition_sql(self, expression: sqlglot.expressions.AddPartition) -> str:
3588    def addpartition_sql(self, expression: exp.AddPartition) -> str:
3589        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
3590        location = self.sql(expression, "location")
3591        location = f" {location}" if location else ""
3592        return f"ADD {exists}{self.sql(expression.this)}{location}"
def distinct_sql(self, expression: sqlglot.expressions.Distinct) -> str:
3594    def distinct_sql(self, expression: exp.Distinct) -> str:
3595        this = self.expressions(expression, flat=True)
3596
3597        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
3598            case = exp.case()
3599            for arg in expression.expressions:
3600                case = case.when(arg.is_(exp.null()), exp.null())
3601            this = self.sql(case.else_(f"({this})"))
3602
3603        this = f" {this}" if this else ""
3604
3605        on = self.sql(expression, "on")
3606        on = f" ON {on}" if on else ""
3607        return f"DISTINCT{this}{on}"
def ignorenulls_sql(self, expression: sqlglot.expressions.IgnoreNulls) -> str:
3609    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
3610        return self._embed_ignore_nulls(expression, "IGNORE NULLS")
def respectnulls_sql(self, expression: sqlglot.expressions.RespectNulls) -> str:
3612    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
3613        return self._embed_ignore_nulls(expression, "RESPECT NULLS")
def havingmax_sql(self, expression: sqlglot.expressions.HavingMax) -> str:
3615    def havingmax_sql(self, expression: exp.HavingMax) -> str:
3616        this_sql = self.sql(expression, "this")
3617        expression_sql = self.sql(expression, "expression")
3618        kind = "MAX" if expression.args.get("max") else "MIN"
3619        return f"{this_sql} HAVING {kind} {expression_sql}"
def intdiv_sql(self, expression: sqlglot.expressions.IntDiv) -> str:
3621    def intdiv_sql(self, expression: exp.IntDiv) -> str:
3622        return self.sql(
3623            exp.Cast(
3624                this=exp.Div(this=expression.this, expression=expression.expression),
3625                to=exp.DataType(this=exp.DataType.Type.INT),
3626            )
3627        )
def dpipe_sql(self, expression: sqlglot.expressions.DPipe) -> str:
3629    def dpipe_sql(self, expression: exp.DPipe) -> str:
3630        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
3631            return self.func(
3632                "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten())
3633            )
3634        return self.binary(expression, "||")
def div_sql(self, expression: sqlglot.expressions.Div) -> str:
3636    def div_sql(self, expression: exp.Div) -> str:
3637        l, r = expression.left, expression.right
3638
3639        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
3640            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
3641
3642        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
3643            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
3644                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
3645
3646        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
3647            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
3648                return self.sql(
3649                    exp.cast(
3650                        l / r,
3651                        to=exp.DataType.Type.BIGINT,
3652                    )
3653                )
3654
3655        return self.binary(expression, "/")
def safedivide_sql(self, expression: sqlglot.expressions.SafeDivide) -> str:
3657    def safedivide_sql(self, expression: exp.SafeDivide) -> str:
3658        n = exp._wrap(expression.this, exp.Binary)
3659        d = exp._wrap(expression.expression, exp.Binary)
3660        return self.sql(exp.If(this=d.neq(0), true=n / d, false=exp.Null()))
def overlaps_sql(self, expression: sqlglot.expressions.Overlaps) -> str:
3662    def overlaps_sql(self, expression: exp.Overlaps) -> str:
3663        return self.binary(expression, "OVERLAPS")
def distance_sql(self, expression: sqlglot.expressions.Distance) -> str:
3665    def distance_sql(self, expression: exp.Distance) -> str:
3666        return self.binary(expression, "<->")
def dot_sql(self, expression: sqlglot.expressions.Dot) -> str:
3668    def dot_sql(self, expression: exp.Dot) -> str:
3669        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
def eq_sql(self, expression: sqlglot.expressions.EQ) -> str:
3671    def eq_sql(self, expression: exp.EQ) -> str:
3672        return self.binary(expression, "=")
def propertyeq_sql(self, expression: sqlglot.expressions.PropertyEQ) -> str:
3674    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
3675        return self.binary(expression, ":=")
def escape_sql(self, expression: sqlglot.expressions.Escape) -> str:
3677    def escape_sql(self, expression: exp.Escape) -> str:
3678        return self.binary(expression, "ESCAPE")
def glob_sql(self, expression: sqlglot.expressions.Glob) -> str:
3680    def glob_sql(self, expression: exp.Glob) -> str:
3681        return self.binary(expression, "GLOB")
def gt_sql(self, expression: sqlglot.expressions.GT) -> str:
3683    def gt_sql(self, expression: exp.GT) -> str:
3684        return self.binary(expression, ">")
def gte_sql(self, expression: sqlglot.expressions.GTE) -> str:
3686    def gte_sql(self, expression: exp.GTE) -> str:
3687        return self.binary(expression, ">=")
def is_sql(self, expression: sqlglot.expressions.Is) -> str:
3689    def is_sql(self, expression: exp.Is) -> str:
3690        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
3691            return self.sql(
3692                expression.this if expression.expression.this else exp.not_(expression.this)
3693            )
3694        return self.binary(expression, "IS")
def like_sql(self, expression: sqlglot.expressions.Like) -> str:
3723    def like_sql(self, expression: exp.Like) -> str:
3724        return self._like_sql(expression)
def ilike_sql(self, expression: sqlglot.expressions.ILike) -> str:
3726    def ilike_sql(self, expression: exp.ILike) -> str:
3727        return self._like_sql(expression)
def similarto_sql(self, expression: sqlglot.expressions.SimilarTo) -> str:
3729    def similarto_sql(self, expression: exp.SimilarTo) -> str:
3730        return self.binary(expression, "SIMILAR TO")
def lt_sql(self, expression: sqlglot.expressions.LT) -> str:
3732    def lt_sql(self, expression: exp.LT) -> str:
3733        return self.binary(expression, "<")
def lte_sql(self, expression: sqlglot.expressions.LTE) -> str:
3735    def lte_sql(self, expression: exp.LTE) -> str:
3736        return self.binary(expression, "<=")
def mod_sql(self, expression: sqlglot.expressions.Mod) -> str:
3738    def mod_sql(self, expression: exp.Mod) -> str:
3739        return self.binary(expression, "%")
def mul_sql(self, expression: sqlglot.expressions.Mul) -> str:
3741    def mul_sql(self, expression: exp.Mul) -> str:
3742        return self.binary(expression, "*")
def neq_sql(self, expression: sqlglot.expressions.NEQ) -> str:
3744    def neq_sql(self, expression: exp.NEQ) -> str:
3745        return self.binary(expression, "<>")
def nullsafeeq_sql(self, expression: sqlglot.expressions.NullSafeEQ) -> str:
3747    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
3748        return self.binary(expression, "IS NOT DISTINCT FROM")
def nullsafeneq_sql(self, expression: sqlglot.expressions.NullSafeNEQ) -> str:
3750    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
3751        return self.binary(expression, "IS DISTINCT FROM")
def slice_sql(self, expression: sqlglot.expressions.Slice) -> str:
3753    def slice_sql(self, expression: exp.Slice) -> str:
3754        return self.binary(expression, ":")
def sub_sql(self, expression: sqlglot.expressions.Sub) -> str:
3756    def sub_sql(self, expression: exp.Sub) -> str:
3757        return self.binary(expression, "-")
def trycast_sql(self, expression: sqlglot.expressions.TryCast) -> str:
3759    def trycast_sql(self, expression: exp.TryCast) -> str:
3760        return self.cast_sql(expression, safe_prefix="TRY_")
def jsoncast_sql(self, expression: sqlglot.expressions.JSONCast) -> str:
3762    def jsoncast_sql(self, expression: exp.JSONCast) -> str:
3763        return self.cast_sql(expression)
def try_sql(self, expression: sqlglot.expressions.Try) -> str:
3765    def try_sql(self, expression: exp.Try) -> str:
3766        if not self.TRY_SUPPORTED:
3767            self.unsupported("Unsupported TRY function")
3768            return self.sql(expression, "this")
3769
3770        return self.func("TRY", expression.this)
def log_sql(self, expression: sqlglot.expressions.Log) -> str:
3772    def log_sql(self, expression: exp.Log) -> str:
3773        this = expression.this
3774        expr = expression.expression
3775
3776        if self.dialect.LOG_BASE_FIRST is False:
3777            this, expr = expr, this
3778        elif self.dialect.LOG_BASE_FIRST is None and expr:
3779            if this.name in ("2", "10"):
3780                return self.func(f"LOG{this.name}", expr)
3781
3782            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
3783
3784        return self.func("LOG", this, expr)
def use_sql(self, expression: sqlglot.expressions.Use) -> str:
3786    def use_sql(self, expression: exp.Use) -> str:
3787        kind = self.sql(expression, "kind")
3788        kind = f" {kind}" if kind else ""
3789        this = self.sql(expression, "this") or self.expressions(expression, flat=True)
3790        this = f" {this}" if this else ""
3791        return f"USE{kind}{this}"
def binary(self, expression: sqlglot.expressions.Binary, op: str) -> str:
3793    def binary(self, expression: exp.Binary, op: str) -> str:
3794        sqls: t.List[str] = []
3795        stack: t.List[t.Union[str, exp.Expression]] = [expression]
3796        binary_type = type(expression)
3797
3798        while stack:
3799            node = stack.pop()
3800
3801            if type(node) is binary_type:
3802                op_func = node.args.get("operator")
3803                if op_func:
3804                    op = f"OPERATOR({self.sql(op_func)})"
3805
3806                stack.append(node.right)
3807                stack.append(f" {self.maybe_comment(op, comments=node.comments)} ")
3808                stack.append(node.left)
3809            else:
3810                sqls.append(self.sql(node))
3811
3812        return "".join(sqls)
def ceil_floor( self, expression: sqlglot.expressions.Ceil | sqlglot.expressions.Floor) -> str:
3814    def ceil_floor(self, expression: exp.Ceil | exp.Floor) -> str:
3815        to_clause = self.sql(expression, "to")
3816        if to_clause:
3817            return f"{expression.sql_name()}({self.sql(expression, 'this')} TO {to_clause})"
3818
3819        return self.function_fallback_sql(expression)
def function_fallback_sql(self, expression: sqlglot.expressions.Func) -> str:
3821    def function_fallback_sql(self, expression: exp.Func) -> str:
3822        args = []
3823
3824        for key in expression.arg_types:
3825            arg_value = expression.args.get(key)
3826
3827            if isinstance(arg_value, list):
3828                for value in arg_value:
3829                    args.append(value)
3830            elif arg_value is not None:
3831                args.append(arg_value)
3832
3833        if self.dialect.PRESERVE_ORIGINAL_NAMES:
3834            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
3835        else:
3836            name = expression.sql_name()
3837
3838        return self.func(name, *args)
def func( self, name: str, *args: Union[str, sqlglot.expressions.Expression, NoneType], prefix: str = '(', suffix: str = ')', normalize: bool = True) -> str:
3840    def func(
3841        self,
3842        name: str,
3843        *args: t.Optional[exp.Expression | str],
3844        prefix: str = "(",
3845        suffix: str = ")",
3846        normalize: bool = True,
3847    ) -> str:
3848        name = self.normalize_func(name) if normalize else name
3849        return f"{name}{prefix}{self.format_args(*args)}{suffix}"
def format_args( self, *args: Union[str, sqlglot.expressions.Expression, NoneType], sep: str = ', ') -> str:
3851    def format_args(self, *args: t.Optional[str | exp.Expression], sep: str = ", ") -> str:
3852        arg_sqls = tuple(
3853            self.sql(arg) for arg in args if arg is not None and not isinstance(arg, bool)
3854        )
3855        if self.pretty and self.too_wide(arg_sqls):
3856            return self.indent(
3857                "\n" + f"{sep.strip()}\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True
3858            )
3859        return sep.join(arg_sqls)
def too_wide(self, args: Iterable) -> bool:
3861    def too_wide(self, args: t.Iterable) -> bool:
3862        return sum(len(arg) for arg in args) > self.max_text_width
def format_time( self, expression: sqlglot.expressions.Expression, inverse_time_mapping: Optional[Dict[str, str]] = None, inverse_time_trie: Optional[Dict] = None) -> Optional[str]:
3864    def format_time(
3865        self,
3866        expression: exp.Expression,
3867        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
3868        inverse_time_trie: t.Optional[t.Dict] = None,
3869    ) -> t.Optional[str]:
3870        return format_time(
3871            self.sql(expression, "format"),
3872            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
3873            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
3874        )
def expressions( self, expression: Optional[sqlglot.expressions.Expression] = None, key: Optional[str] = None, sqls: Optional[Collection[Union[str, sqlglot.expressions.Expression]]] = None, flat: bool = False, indent: bool = True, skip_first: bool = False, skip_last: bool = False, sep: str = ', ', prefix: str = '', dynamic: bool = False, new_line: bool = False) -> str:
3876    def expressions(
3877        self,
3878        expression: t.Optional[exp.Expression] = None,
3879        key: t.Optional[str] = None,
3880        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
3881        flat: bool = False,
3882        indent: bool = True,
3883        skip_first: bool = False,
3884        skip_last: bool = False,
3885        sep: str = ", ",
3886        prefix: str = "",
3887        dynamic: bool = False,
3888        new_line: bool = False,
3889    ) -> str:
3890        expressions = expression.args.get(key or "expressions") if expression else sqls
3891
3892        if not expressions:
3893            return ""
3894
3895        if flat:
3896            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
3897
3898        num_sqls = len(expressions)
3899        result_sqls = []
3900
3901        for i, e in enumerate(expressions):
3902            sql = self.sql(e, comment=False)
3903            if not sql:
3904                continue
3905
3906            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
3907
3908            if self.pretty:
3909                if self.leading_comma:
3910                    result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}")
3911                else:
3912                    result_sqls.append(
3913                        f"{prefix}{sql}{(sep.rstrip() if comments else sep) if i + 1 < num_sqls else ''}{comments}"
3914                    )
3915            else:
3916                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
3917
3918        if self.pretty and (not dynamic or self.too_wide(result_sqls)):
3919            if new_line:
3920                result_sqls.insert(0, "")
3921                result_sqls.append("")
3922            result_sql = "\n".join(s.rstrip() for s in result_sqls)
3923        else:
3924            result_sql = "".join(result_sqls)
3925
3926        return (
3927            self.indent(result_sql, skip_first=skip_first, skip_last=skip_last)
3928            if indent
3929            else result_sql
3930        )
def op_expressions( self, op: str, expression: sqlglot.expressions.Expression, flat: bool = False) -> str:
3932    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
3933        flat = flat or isinstance(expression.parent, exp.Properties)
3934        expressions_sql = self.expressions(expression, flat=flat)
3935        if flat:
3936            return f"{op} {expressions_sql}"
3937        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
def naked_property(self, expression: sqlglot.expressions.Property) -> str:
3939    def naked_property(self, expression: exp.Property) -> str:
3940        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
3941        if not property_name:
3942            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
3943        return f"{property_name} {self.sql(expression, 'this')}"
def tag_sql(self, expression: sqlglot.expressions.Tag) -> str:
3945    def tag_sql(self, expression: exp.Tag) -> str:
3946        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
def token_sql(self, token_type: sqlglot.tokens.TokenType) -> str:
3948    def token_sql(self, token_type: TokenType) -> str:
3949        return self.TOKEN_MAPPING.get(token_type, token_type.name)
def userdefinedfunction_sql(self, expression: sqlglot.expressions.UserDefinedFunction) -> str:
3951    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
3952        this = self.sql(expression, "this")
3953        expressions = self.no_identify(self.expressions, expression)
3954        expressions = (
3955            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
3956        )
3957        return f"{this}{expressions}" if expressions.strip() != "" else this
def joinhint_sql(self, expression: sqlglot.expressions.JoinHint) -> str:
3959    def joinhint_sql(self, expression: exp.JoinHint) -> str:
3960        this = self.sql(expression, "this")
3961        expressions = self.expressions(expression, flat=True)
3962        return f"{this}({expressions})"
def kwarg_sql(self, expression: sqlglot.expressions.Kwarg) -> str:
3964    def kwarg_sql(self, expression: exp.Kwarg) -> str:
3965        return self.binary(expression, "=>")
def when_sql(self, expression: sqlglot.expressions.When) -> str:
3967    def when_sql(self, expression: exp.When) -> str:
3968        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
3969        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
3970        condition = self.sql(expression, "condition")
3971        condition = f" AND {condition}" if condition else ""
3972
3973        then_expression = expression.args.get("then")
3974        if isinstance(then_expression, exp.Insert):
3975            this = self.sql(then_expression, "this")
3976            this = f"INSERT {this}" if this else "INSERT"
3977            then = self.sql(then_expression, "expression")
3978            then = f"{this} VALUES {then}" if then else this
3979        elif isinstance(then_expression, exp.Update):
3980            if isinstance(then_expression.args.get("expressions"), exp.Star):
3981                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
3982            else:
3983                then = f"UPDATE SET{self.sep()}{self.expressions(then_expression)}"
3984        else:
3985            then = self.sql(then_expression)
3986        return f"WHEN {matched}{source}{condition} THEN {then}"
def whens_sql(self, expression: sqlglot.expressions.Whens) -> str:
3988    def whens_sql(self, expression: exp.Whens) -> str:
3989        return self.expressions(expression, sep=" ", indent=False)
def merge_sql(self, expression: sqlglot.expressions.Merge) -> str:
3991    def merge_sql(self, expression: exp.Merge) -> str:
3992        table = expression.this
3993        table_alias = ""
3994
3995        hints = table.args.get("hints")
3996        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
3997            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
3998            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
3999
4000        this = self.sql(table)
4001        using = f"USING {self.sql(expression, 'using')}"
4002        on = f"ON {self.sql(expression, 'on')}"
4003        whens = self.sql(expression, "whens")
4004
4005        returning = self.sql(expression, "returning")
4006        if returning:
4007            whens = f"{whens}{returning}"
4008
4009        sep = self.sep()
4010
4011        return self.prepend_ctes(
4012            expression,
4013            f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{whens}",
4014        )
@unsupported_args('format')
def tochar_sql(self, expression: sqlglot.expressions.ToChar) -> str:
4016    @unsupported_args("format")
4017    def tochar_sql(self, expression: exp.ToChar) -> str:
4018        return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT))
def tonumber_sql(self, expression: sqlglot.expressions.ToNumber) -> str:
4020    def tonumber_sql(self, expression: exp.ToNumber) -> str:
4021        if not self.SUPPORTS_TO_NUMBER:
4022            self.unsupported("Unsupported TO_NUMBER function")
4023            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
4024
4025        fmt = expression.args.get("format")
4026        if not fmt:
4027            self.unsupported("Conversion format is required for TO_NUMBER")
4028            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
4029
4030        return self.func("TO_NUMBER", expression.this, fmt)
def dictproperty_sql(self, expression: sqlglot.expressions.DictProperty) -> str:
4032    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
4033        this = self.sql(expression, "this")
4034        kind = self.sql(expression, "kind")
4035        settings_sql = self.expressions(expression, key="settings", sep=" ")
4036        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
4037        return f"{this}({kind}{args})"
def dictrange_sql(self, expression: sqlglot.expressions.DictRange) -> str:
4039    def dictrange_sql(self, expression: exp.DictRange) -> str:
4040        this = self.sql(expression, "this")
4041        max = self.sql(expression, "max")
4042        min = self.sql(expression, "min")
4043        return f"{this}(MIN {min} MAX {max})"
def dictsubproperty_sql(self, expression: sqlglot.expressions.DictSubProperty) -> str:
4045    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
4046        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
def duplicatekeyproperty_sql(self, expression: sqlglot.expressions.DuplicateKeyProperty) -> str:
4048    def duplicatekeyproperty_sql(self, expression: exp.DuplicateKeyProperty) -> str:
4049        return f"DUPLICATE KEY ({self.expressions(expression, flat=True)})"
def uniquekeyproperty_sql( self, expression: sqlglot.expressions.UniqueKeyProperty, prefix: str = 'UNIQUE KEY') -> str:
4052    def uniquekeyproperty_sql(
4053        self, expression: exp.UniqueKeyProperty, prefix: str = "UNIQUE KEY"
4054    ) -> str:
4055        return f"{prefix} ({self.expressions(expression, flat=True)})"
def distributedbyproperty_sql(self, expression: sqlglot.expressions.DistributedByProperty) -> str:
4058    def distributedbyproperty_sql(self, expression: exp.DistributedByProperty) -> str:
4059        expressions = self.expressions(expression, flat=True)
4060        expressions = f" {self.wrap(expressions)}" if expressions else ""
4061        buckets = self.sql(expression, "buckets")
4062        kind = self.sql(expression, "kind")
4063        buckets = f" BUCKETS {buckets}" if buckets else ""
4064        order = self.sql(expression, "order")
4065        return f"DISTRIBUTED BY {kind}{expressions}{buckets}{order}"
def oncluster_sql(self, expression: sqlglot.expressions.OnCluster) -> str:
4067    def oncluster_sql(self, expression: exp.OnCluster) -> str:
4068        return ""
def clusteredbyproperty_sql(self, expression: sqlglot.expressions.ClusteredByProperty) -> str:
4070    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
4071        expressions = self.expressions(expression, key="expressions", flat=True)
4072        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
4073        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
4074        buckets = self.sql(expression, "buckets")
4075        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
def anyvalue_sql(self, expression: sqlglot.expressions.AnyValue) -> str:
4077    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
4078        this = self.sql(expression, "this")
4079        having = self.sql(expression, "having")
4080
4081        if having:
4082            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
4083
4084        return self.func("ANY_VALUE", this)
def querytransform_sql(self, expression: sqlglot.expressions.QueryTransform) -> str:
4086    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
4087        transform = self.func("TRANSFORM", *expression.expressions)
4088        row_format_before = self.sql(expression, "row_format_before")
4089        row_format_before = f" {row_format_before}" if row_format_before else ""
4090        record_writer = self.sql(expression, "record_writer")
4091        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
4092        using = f" USING {self.sql(expression, 'command_script')}"
4093        schema = self.sql(expression, "schema")
4094        schema = f" AS {schema}" if schema else ""
4095        row_format_after = self.sql(expression, "row_format_after")
4096        row_format_after = f" {row_format_after}" if row_format_after else ""
4097        record_reader = self.sql(expression, "record_reader")
4098        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
4099        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
def indexconstraintoption_sql(self, expression: sqlglot.expressions.IndexConstraintOption) -> str:
4101    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
4102        key_block_size = self.sql(expression, "key_block_size")
4103        if key_block_size:
4104            return f"KEY_BLOCK_SIZE = {key_block_size}"
4105
4106        using = self.sql(expression, "using")
4107        if using:
4108            return f"USING {using}"
4109
4110        parser = self.sql(expression, "parser")
4111        if parser:
4112            return f"WITH PARSER {parser}"
4113
4114        comment = self.sql(expression, "comment")
4115        if comment:
4116            return f"COMMENT {comment}"
4117
4118        visible = expression.args.get("visible")
4119        if visible is not None:
4120            return "VISIBLE" if visible else "INVISIBLE"
4121
4122        engine_attr = self.sql(expression, "engine_attr")
4123        if engine_attr:
4124            return f"ENGINE_ATTRIBUTE = {engine_attr}"
4125
4126        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
4127        if secondary_engine_attr:
4128            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
4129
4130        self.unsupported("Unsupported index constraint option.")
4131        return ""
def checkcolumnconstraint_sql(self, expression: sqlglot.expressions.CheckColumnConstraint) -> str:
4133    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
4134        enforced = " ENFORCED" if expression.args.get("enforced") else ""
4135        return f"CHECK ({self.sql(expression, 'this')}){enforced}"
def indexcolumnconstraint_sql(self, expression: sqlglot.expressions.IndexColumnConstraint) -> str:
4137    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
4138        kind = self.sql(expression, "kind")
4139        kind = f"{kind} INDEX" if kind else "INDEX"
4140        this = self.sql(expression, "this")
4141        this = f" {this}" if this else ""
4142        index_type = self.sql(expression, "index_type")
4143        index_type = f" USING {index_type}" if index_type else ""
4144        expressions = self.expressions(expression, flat=True)
4145        expressions = f" ({expressions})" if expressions else ""
4146        options = self.expressions(expression, key="options", sep=" ")
4147        options = f" {options}" if options else ""
4148        return f"{kind}{this}{index_type}{expressions}{options}"
def nvl2_sql(self, expression: sqlglot.expressions.Nvl2) -> str:
4150    def nvl2_sql(self, expression: exp.Nvl2) -> str:
4151        if self.NVL2_SUPPORTED:
4152            return self.function_fallback_sql(expression)
4153
4154        case = exp.Case().when(
4155            expression.this.is_(exp.null()).not_(copy=False),
4156            expression.args["true"],
4157            copy=False,
4158        )
4159        else_cond = expression.args.get("false")
4160        if else_cond:
4161            case.else_(else_cond, copy=False)
4162
4163        return self.sql(case)
def comprehension_sql(self, expression: sqlglot.expressions.Comprehension) -> str:
4165    def comprehension_sql(self, expression: exp.Comprehension) -> str:
4166        this = self.sql(expression, "this")
4167        expr = self.sql(expression, "expression")
4168        iterator = self.sql(expression, "iterator")
4169        condition = self.sql(expression, "condition")
4170        condition = f" IF {condition}" if condition else ""
4171        return f"{this} FOR {expr} IN {iterator}{condition}"
def columnprefix_sql(self, expression: sqlglot.expressions.ColumnPrefix) -> str:
4173    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
4174        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
def opclass_sql(self, expression: sqlglot.expressions.Opclass) -> str:
4176    def opclass_sql(self, expression: exp.Opclass) -> str:
4177        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
def predict_sql(self, expression: sqlglot.expressions.Predict) -> str:
4179    def predict_sql(self, expression: exp.Predict) -> str:
4180        model = self.sql(expression, "this")
4181        model = f"MODEL {model}"
4182        table = self.sql(expression, "expression")
4183        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
4184        parameters = self.sql(expression, "params_struct")
4185        return self.func("PREDICT", model, table, parameters or None)
def generateembedding_sql(self, expression: sqlglot.expressions.GenerateEmbedding) -> str:
4187    def generateembedding_sql(self, expression: exp.GenerateEmbedding) -> str:
4188        model = self.sql(expression, "this")
4189        model = f"MODEL {model}"
4190        table = self.sql(expression, "expression")
4191        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
4192        parameters = self.sql(expression, "params_struct")
4193        return self.func("GENERATE_EMBEDDING", model, table, parameters or None)
def featuresattime_sql(self, expression: sqlglot.expressions.FeaturesAtTime) -> str:
4195    def featuresattime_sql(self, expression: exp.FeaturesAtTime) -> str:
4196        this_sql = self.sql(expression, "this")
4197        if isinstance(expression.this, exp.Table):
4198            this_sql = f"TABLE {this_sql}"
4199
4200        return self.func(
4201            "FEATURES_AT_TIME",
4202            this_sql,
4203            expression.args.get("time"),
4204            expression.args.get("num_rows"),
4205            expression.args.get("ignore_feature_nulls"),
4206        )
def vectorsearch_sql(self, expression: sqlglot.expressions.VectorSearch) -> str:
4208    def vectorsearch_sql(self, expression: exp.VectorSearch) -> str:
4209        this_sql = self.sql(expression, "this")
4210        if isinstance(expression.this, exp.Table):
4211            this_sql = f"TABLE {this_sql}"
4212
4213        query_table = self.sql(expression, "query_table")
4214        if isinstance(expression.args["query_table"], exp.Table):
4215            query_table = f"TABLE {query_table}"
4216
4217        return self.func(
4218            "VECTOR_SEARCH",
4219            this_sql,
4220            expression.args.get("column_to_search"),
4221            query_table,
4222            expression.args.get("query_column_to_search"),
4223            expression.args.get("top_k"),
4224            expression.args.get("distance_type"),
4225            expression.args.get("options"),
4226        )
def forin_sql(self, expression: sqlglot.expressions.ForIn) -> str:
4228    def forin_sql(self, expression: exp.ForIn) -> str:
4229        this = self.sql(expression, "this")
4230        expression_sql = self.sql(expression, "expression")
4231        return f"FOR {this} DO {expression_sql}"
def refresh_sql(self, expression: sqlglot.expressions.Refresh) -> str:
4233    def refresh_sql(self, expression: exp.Refresh) -> str:
4234        this = self.sql(expression, "this")
4235        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
4236        return f"REFRESH {table}{this}"
def toarray_sql(self, expression: sqlglot.expressions.ToArray) -> str:
4238    def toarray_sql(self, expression: exp.ToArray) -> str:
4239        arg = expression.this
4240        if not arg.type:
4241            from sqlglot.optimizer.annotate_types import annotate_types
4242
4243            arg = annotate_types(arg, dialect=self.dialect)
4244
4245        if arg.is_type(exp.DataType.Type.ARRAY):
4246            return self.sql(arg)
4247
4248        cond_for_null = arg.is_(exp.null())
4249        return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
def tsordstotime_sql(self, expression: sqlglot.expressions.TsOrDsToTime) -> str:
4251    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
4252        this = expression.this
4253        time_format = self.format_time(expression)
4254
4255        if time_format:
4256            return self.sql(
4257                exp.cast(
4258                    exp.StrToTime(this=this, format=expression.args["format"]),
4259                    exp.DataType.Type.TIME,
4260                )
4261            )
4262
4263        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
4264            return self.sql(this)
4265
4266        return self.sql(exp.cast(this, exp.DataType.Type.TIME))
def tsordstotimestamp_sql(self, expression: sqlglot.expressions.TsOrDsToTimestamp) -> str:
4268    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
4269        this = expression.this
4270        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
4271            return self.sql(this)
4272
4273        return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP, dialect=self.dialect))
def tsordstodatetime_sql(self, expression: sqlglot.expressions.TsOrDsToDatetime) -> str:
4275    def tsordstodatetime_sql(self, expression: exp.TsOrDsToDatetime) -> str:
4276        this = expression.this
4277        if isinstance(this, exp.TsOrDsToDatetime) or this.is_type(exp.DataType.Type.DATETIME):
4278            return self.sql(this)
4279
4280        return self.sql(exp.cast(this, exp.DataType.Type.DATETIME, dialect=self.dialect))
def tsordstodate_sql(self, expression: sqlglot.expressions.TsOrDsToDate) -> str:
4282    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
4283        this = expression.this
4284        time_format = self.format_time(expression)
4285
4286        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
4287            return self.sql(
4288                exp.cast(
4289                    exp.StrToTime(this=this, format=expression.args["format"]),
4290                    exp.DataType.Type.DATE,
4291                )
4292            )
4293
4294        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
4295            return self.sql(this)
4296
4297        return self.sql(exp.cast(this, exp.DataType.Type.DATE))
def unixdate_sql(self, expression: sqlglot.expressions.UnixDate) -> str:
4299    def unixdate_sql(self, expression: exp.UnixDate) -> str:
4300        return self.sql(
4301            exp.func(
4302                "DATEDIFF",
4303                expression.this,
4304                exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
4305                "day",
4306            )
4307        )
def lastday_sql(self, expression: sqlglot.expressions.LastDay) -> str:
4309    def lastday_sql(self, expression: exp.LastDay) -> str:
4310        if self.LAST_DAY_SUPPORTS_DATE_PART:
4311            return self.function_fallback_sql(expression)
4312
4313        unit = expression.text("unit")
4314        if unit and unit != "MONTH":
4315            self.unsupported("Date parts are not supported in LAST_DAY.")
4316
4317        return self.func("LAST_DAY", expression.this)
def dateadd_sql(self, expression: sqlglot.expressions.DateAdd) -> str:
4319    def dateadd_sql(self, expression: exp.DateAdd) -> str:
4320        from sqlglot.dialects.dialect import unit_to_str
4321
4322        return self.func(
4323            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
4324        )
def arrayany_sql(self, expression: sqlglot.expressions.ArrayAny) -> str:
4326    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
4327        if self.CAN_IMPLEMENT_ARRAY_ANY:
4328            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
4329            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
4330            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
4331            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
4332
4333        from sqlglot.dialects import Dialect
4334
4335        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
4336        if self.dialect.__class__ != Dialect:
4337            self.unsupported("ARRAY_ANY is unsupported")
4338
4339        return self.function_fallback_sql(expression)
def struct_sql(self, expression: sqlglot.expressions.Struct) -> str:
4341    def struct_sql(self, expression: exp.Struct) -> str:
4342        expression.set(
4343            "expressions",
4344            [
4345                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
4346                if isinstance(e, exp.PropertyEQ)
4347                else e
4348                for e in expression.expressions
4349            ],
4350        )
4351
4352        return self.function_fallback_sql(expression)
def partitionrange_sql(self, expression: sqlglot.expressions.PartitionRange) -> str:
4354    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
4355        low = self.sql(expression, "this")
4356        high = self.sql(expression, "expression")
4357
4358        return f"{low} TO {high}"
def truncatetable_sql(self, expression: sqlglot.expressions.TruncateTable) -> str:
4360    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
4361        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
4362        tables = f" {self.expressions(expression)}"
4363
4364        exists = " IF EXISTS" if expression.args.get("exists") else ""
4365
4366        on_cluster = self.sql(expression, "cluster")
4367        on_cluster = f" {on_cluster}" if on_cluster else ""
4368
4369        identity = self.sql(expression, "identity")
4370        identity = f" {identity} IDENTITY" if identity else ""
4371
4372        option = self.sql(expression, "option")
4373        option = f" {option}" if option else ""
4374
4375        partition = self.sql(expression, "partition")
4376        partition = f" {partition}" if partition else ""
4377
4378        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
def convert_sql(self, expression: sqlglot.expressions.Convert) -> str:
4382    def convert_sql(self, expression: exp.Convert) -> str:
4383        to = expression.this
4384        value = expression.expression
4385        style = expression.args.get("style")
4386        safe = expression.args.get("safe")
4387        strict = expression.args.get("strict")
4388
4389        if not to or not value:
4390            return ""
4391
4392        # Retrieve length of datatype and override to default if not specified
4393        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
4394            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
4395
4396        transformed: t.Optional[exp.Expression] = None
4397        cast = exp.Cast if strict else exp.TryCast
4398
4399        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
4400        if isinstance(style, exp.Literal) and style.is_int:
4401            from sqlglot.dialects.tsql import TSQL
4402
4403            style_value = style.name
4404            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
4405            if not converted_style:
4406                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
4407
4408            fmt = exp.Literal.string(converted_style)
4409
4410            if to.this == exp.DataType.Type.DATE:
4411                transformed = exp.StrToDate(this=value, format=fmt)
4412            elif to.this in (exp.DataType.Type.DATETIME, exp.DataType.Type.DATETIME2):
4413                transformed = exp.StrToTime(this=value, format=fmt)
4414            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
4415                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
4416            elif to.this == exp.DataType.Type.TEXT:
4417                transformed = exp.TimeToStr(this=value, format=fmt)
4418
4419        if not transformed:
4420            transformed = cast(this=value, to=to, safe=safe)
4421
4422        return self.sql(transformed)
def copyparameter_sql(self, expression: sqlglot.expressions.CopyParameter) -> str:
4490    def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
4491        option = self.sql(expression, "this")
4492
4493        if expression.expressions:
4494            upper = option.upper()
4495
4496            # Snowflake FILE_FORMAT options are separated by whitespace
4497            sep = " " if upper == "FILE_FORMAT" else ", "
4498
4499            # Databricks copy/format options do not set their list of values with EQ
4500            op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = "
4501            values = self.expressions(expression, flat=True, sep=sep)
4502            return f"{option}{op}({values})"
4503
4504        value = self.sql(expression, "expression")
4505
4506        if not value:
4507            return option
4508
4509        op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " "
4510
4511        return f"{option}{op}{value}"
def credentials_sql(self, expression: sqlglot.expressions.Credentials) -> str:
4513    def credentials_sql(self, expression: exp.Credentials) -> str:
4514        cred_expr = expression.args.get("credentials")
4515        if isinstance(cred_expr, exp.Literal):
4516            # Redshift case: CREDENTIALS <string>
4517            credentials = self.sql(expression, "credentials")
4518            credentials = f"CREDENTIALS {credentials}" if credentials else ""
4519        else:
4520            # Snowflake case: CREDENTIALS = (...)
4521            credentials = self.expressions(expression, key="credentials", flat=True, sep=" ")
4522            credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else ""
4523
4524        storage = self.sql(expression, "storage")
4525        storage = f"STORAGE_INTEGRATION = {storage}" if storage else ""
4526
4527        encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
4528        encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
4529
4530        iam_role = self.sql(expression, "iam_role")
4531        iam_role = f"IAM_ROLE {iam_role}" if iam_role else ""
4532
4533        region = self.sql(expression, "region")
4534        region = f" REGION {region}" if region else ""
4535
4536        return f"{credentials}{storage}{encryption}{iam_role}{region}"
def copy_sql(self, expression: sqlglot.expressions.Copy) -> str:
4538    def copy_sql(self, expression: exp.Copy) -> str:
4539        this = self.sql(expression, "this")
4540        this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
4541
4542        credentials = self.sql(expression, "credentials")
4543        credentials = self.seg(credentials) if credentials else ""
4544        kind = self.seg("FROM" if expression.args.get("kind") else "TO")
4545        files = self.expressions(expression, key="files", flat=True)
4546
4547        sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
4548        params = self.expressions(
4549            expression,
4550            key="params",
4551            sep=sep,
4552            new_line=True,
4553            skip_last=True,
4554            skip_first=True,
4555            indent=self.COPY_PARAMS_ARE_WRAPPED,
4556        )
4557
4558        if params:
4559            if self.COPY_PARAMS_ARE_WRAPPED:
4560                params = f" WITH ({params})"
4561            elif not self.pretty:
4562                params = f" {params}"
4563
4564        return f"COPY{this}{kind} {files}{credentials}{params}"
def semicolon_sql(self, expression: sqlglot.expressions.Semicolon) -> str:
4566    def semicolon_sql(self, expression: exp.Semicolon) -> str:
4567        return ""
def datadeletionproperty_sql(self, expression: sqlglot.expressions.DataDeletionProperty) -> str:
4569    def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
4570        on_sql = "ON" if expression.args.get("on") else "OFF"
4571        filter_col: t.Optional[str] = self.sql(expression, "filter_column")
4572        filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
4573        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
4574        retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
4575
4576        if filter_col or retention_period:
4577            on_sql = self.func("ON", filter_col, retention_period)
4578
4579        return f"DATA_DELETION={on_sql}"
def maskingpolicycolumnconstraint_sql( self, expression: sqlglot.expressions.MaskingPolicyColumnConstraint) -> str:
4581    def maskingpolicycolumnconstraint_sql(
4582        self, expression: exp.MaskingPolicyColumnConstraint
4583    ) -> str:
4584        this = self.sql(expression, "this")
4585        expressions = self.expressions(expression, flat=True)
4586        expressions = f" USING ({expressions})" if expressions else ""
4587        return f"MASKING POLICY {this}{expressions}"
def gapfill_sql(self, expression: sqlglot.expressions.GapFill) -> str:
4589    def gapfill_sql(self, expression: exp.GapFill) -> str:
4590        this = self.sql(expression, "this")
4591        this = f"TABLE {this}"
4592        return self.func("GAP_FILL", this, *[v for k, v in expression.args.items() if k != "this"])
def scope_resolution(self, rhs: str, scope_name: str) -> str:
4594    def scope_resolution(self, rhs: str, scope_name: str) -> str:
4595        return self.func("SCOPE_RESOLUTION", scope_name or None, rhs)
def scoperesolution_sql(self, expression: sqlglot.expressions.ScopeResolution) -> str:
4597    def scoperesolution_sql(self, expression: exp.ScopeResolution) -> str:
4598        this = self.sql(expression, "this")
4599        expr = expression.expression
4600
4601        if isinstance(expr, exp.Func):
4602            # T-SQL's CLR functions are case sensitive
4603            expr = f"{self.sql(expr, 'this')}({self.format_args(*expr.expressions)})"
4604        else:
4605            expr = self.sql(expression, "expression")
4606
4607        return self.scope_resolution(expr, this)
def parsejson_sql(self, expression: sqlglot.expressions.ParseJSON) -> str:
4609    def parsejson_sql(self, expression: exp.ParseJSON) -> str:
4610        if self.PARSE_JSON_NAME is None:
4611            return self.sql(expression.this)
4612
4613        return self.func(self.PARSE_JSON_NAME, expression.this, expression.expression)
def rand_sql(self, expression: sqlglot.expressions.Rand) -> str:
4615    def rand_sql(self, expression: exp.Rand) -> str:
4616        lower = self.sql(expression, "lower")
4617        upper = self.sql(expression, "upper")
4618
4619        if lower and upper:
4620            return f"({upper} - {lower}) * {self.func('RAND', expression.this)} + {lower}"
4621        return self.func("RAND", expression.this)
def changes_sql(self, expression: sqlglot.expressions.Changes) -> str:
4623    def changes_sql(self, expression: exp.Changes) -> str:
4624        information = self.sql(expression, "information")
4625        information = f"INFORMATION => {information}"
4626        at_before = self.sql(expression, "at_before")
4627        at_before = f"{self.seg('')}{at_before}" if at_before else ""
4628        end = self.sql(expression, "end")
4629        end = f"{self.seg('')}{end}" if end else ""
4630
4631        return f"CHANGES ({information}){at_before}{end}"
def pad_sql(self, expression: sqlglot.expressions.Pad) -> str:
4633    def pad_sql(self, expression: exp.Pad) -> str:
4634        prefix = "L" if expression.args.get("is_left") else "R"
4635
4636        fill_pattern = self.sql(expression, "fill_pattern") or None
4637        if not fill_pattern and self.PAD_FILL_PATTERN_IS_REQUIRED:
4638            fill_pattern = "' '"
4639
4640        return self.func(f"{prefix}PAD", expression.this, expression.expression, fill_pattern)
def summarize_sql(self, expression: sqlglot.expressions.Summarize) -> str:
4642    def summarize_sql(self, expression: exp.Summarize) -> str:
4643        table = " TABLE" if expression.args.get("table") else ""
4644        return f"SUMMARIZE{table} {self.sql(expression.this)}"
def explodinggenerateseries_sql(self, expression: sqlglot.expressions.ExplodingGenerateSeries) -> str:
4646    def explodinggenerateseries_sql(self, expression: exp.ExplodingGenerateSeries) -> str:
4647        generate_series = exp.GenerateSeries(**expression.args)
4648
4649        parent = expression.parent
4650        if isinstance(parent, (exp.Alias, exp.TableAlias)):
4651            parent = parent.parent
4652
4653        if self.SUPPORTS_EXPLODING_PROJECTIONS and not isinstance(parent, (exp.Table, exp.Unnest)):
4654            return self.sql(exp.Unnest(expressions=[generate_series]))
4655
4656        if isinstance(parent, exp.Select):
4657            self.unsupported("GenerateSeries projection unnesting is not supported.")
4658
4659        return self.sql(generate_series)
def arrayconcat_sql( self, expression: sqlglot.expressions.ArrayConcat, name: str = 'ARRAY_CONCAT') -> str:
4661    def arrayconcat_sql(self, expression: exp.ArrayConcat, name: str = "ARRAY_CONCAT") -> str:
4662        exprs = expression.expressions
4663        if not self.ARRAY_CONCAT_IS_VAR_LEN:
4664            rhs = reduce(lambda x, y: exp.ArrayConcat(this=x, expressions=[y]), exprs)
4665        else:
4666            rhs = self.expressions(expression)
4667
4668        return self.func(name, expression.this, rhs or None)
def converttimezone_sql(self, expression: sqlglot.expressions.ConvertTimezone) -> str:
4670    def converttimezone_sql(self, expression: exp.ConvertTimezone) -> str:
4671        if self.SUPPORTS_CONVERT_TIMEZONE:
4672            return self.function_fallback_sql(expression)
4673
4674        source_tz = expression.args.get("source_tz")
4675        target_tz = expression.args.get("target_tz")
4676        timestamp = expression.args.get("timestamp")
4677
4678        if source_tz and timestamp:
4679            timestamp = exp.AtTimeZone(
4680                this=exp.cast(timestamp, exp.DataType.Type.TIMESTAMPNTZ), zone=source_tz
4681            )
4682
4683        expr = exp.AtTimeZone(this=timestamp, zone=target_tz)
4684
4685        return self.sql(expr)
def json_sql(self, expression: sqlglot.expressions.JSON) -> str:
4687    def json_sql(self, expression: exp.JSON) -> str:
4688        this = self.sql(expression, "this")
4689        this = f" {this}" if this else ""
4690
4691        _with = expression.args.get("with")
4692
4693        if _with is None:
4694            with_sql = ""
4695        elif not _with:
4696            with_sql = " WITHOUT"
4697        else:
4698            with_sql = " WITH"
4699
4700        unique_sql = " UNIQUE KEYS" if expression.args.get("unique") else ""
4701
4702        return f"JSON{this}{with_sql}{unique_sql}"
def jsonvalue_sql(self, expression: sqlglot.expressions.JSONValue) -> str:
4704    def jsonvalue_sql(self, expression: exp.JSONValue) -> str:
4705        def _generate_on_options(arg: t.Any) -> str:
4706            return arg if isinstance(arg, str) else f"DEFAULT {self.sql(arg)}"
4707
4708        path = self.sql(expression, "path")
4709        returning = self.sql(expression, "returning")
4710        returning = f" RETURNING {returning}" if returning else ""
4711
4712        on_condition = self.sql(expression, "on_condition")
4713        on_condition = f" {on_condition}" if on_condition else ""
4714
4715        return self.func("JSON_VALUE", expression.this, f"{path}{returning}{on_condition}")
def conditionalinsert_sql(self, expression: sqlglot.expressions.ConditionalInsert) -> str:
4717    def conditionalinsert_sql(self, expression: exp.ConditionalInsert) -> str:
4718        else_ = "ELSE " if expression.args.get("else_") else ""
4719        condition = self.sql(expression, "expression")
4720        condition = f"WHEN {condition} THEN " if condition else else_
4721        insert = self.sql(expression, "this")[len("INSERT") :].strip()
4722        return f"{condition}{insert}"
def multitableinserts_sql(self, expression: sqlglot.expressions.MultitableInserts) -> str:
4724    def multitableinserts_sql(self, expression: exp.MultitableInserts) -> str:
4725        kind = self.sql(expression, "kind")
4726        expressions = self.seg(self.expressions(expression, sep=" "))
4727        res = f"INSERT {kind}{expressions}{self.seg(self.sql(expression, 'source'))}"
4728        return res
def oncondition_sql(self, expression: sqlglot.expressions.OnCondition) -> str:
4730    def oncondition_sql(self, expression: exp.OnCondition) -> str:
4731        # Static options like "NULL ON ERROR" are stored as strings, in contrast to "DEFAULT <expr> ON ERROR"
4732        empty = expression.args.get("empty")
4733        empty = (
4734            f"DEFAULT {empty} ON EMPTY"
4735            if isinstance(empty, exp.Expression)
4736            else self.sql(expression, "empty")
4737        )
4738
4739        error = expression.args.get("error")
4740        error = (
4741            f"DEFAULT {error} ON ERROR"
4742            if isinstance(error, exp.Expression)
4743            else self.sql(expression, "error")
4744        )
4745
4746        if error and empty:
4747            error = (
4748                f"{empty} {error}"
4749                if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR
4750                else f"{error} {empty}"
4751            )
4752            empty = ""
4753
4754        null = self.sql(expression, "null")
4755
4756        return f"{empty}{error}{null}"
def jsonextractquote_sql(self, expression: sqlglot.expressions.JSONExtractQuote) -> str:
4758    def jsonextractquote_sql(self, expression: exp.JSONExtractQuote) -> str:
4759        scalar = " ON SCALAR STRING" if expression.args.get("scalar") else ""
4760        return f"{self.sql(expression, 'option')} QUOTES{scalar}"
def jsonexists_sql(self, expression: sqlglot.expressions.JSONExists) -> str:
4762    def jsonexists_sql(self, expression: exp.JSONExists) -> str:
4763        this = self.sql(expression, "this")
4764        path = self.sql(expression, "path")
4765
4766        passing = self.expressions(expression, "passing")
4767        passing = f" PASSING {passing}" if passing else ""
4768
4769        on_condition = self.sql(expression, "on_condition")
4770        on_condition = f" {on_condition}" if on_condition else ""
4771
4772        path = f"{path}{passing}{on_condition}"
4773
4774        return self.func("JSON_EXISTS", this, path)
def arrayagg_sql(self, expression: sqlglot.expressions.ArrayAgg) -> str:
4776    def arrayagg_sql(self, expression: exp.ArrayAgg) -> str:
4777        array_agg = self.function_fallback_sql(expression)
4778
4779        # Add a NULL FILTER on the column to mimic the results going from a dialect that excludes nulls
4780        # on ARRAY_AGG (e.g Spark) to one that doesn't (e.g. DuckDB)
4781        if self.dialect.ARRAY_AGG_INCLUDES_NULLS and expression.args.get("nulls_excluded"):
4782            parent = expression.parent
4783            if isinstance(parent, exp.Filter):
4784                parent_cond = parent.expression.this
4785                parent_cond.replace(parent_cond.and_(expression.this.is_(exp.null()).not_()))
4786            else:
4787                this = expression.this
4788                # Do not add the filter if the input is not a column (e.g. literal, struct etc)
4789                if this.find(exp.Column):
4790                    # DISTINCT is already present in the agg function, do not propagate it to FILTER as well
4791                    this_sql = (
4792                        self.expressions(this)
4793                        if isinstance(this, exp.Distinct)
4794                        else self.sql(expression, "this")
4795                    )
4796
4797                    array_agg = f"{array_agg} FILTER(WHERE {this_sql} IS NOT NULL)"
4798
4799        return array_agg
def apply_sql(self, expression: sqlglot.expressions.Apply) -> str:
4801    def apply_sql(self, expression: exp.Apply) -> str:
4802        this = self.sql(expression, "this")
4803        expr = self.sql(expression, "expression")
4804
4805        return f"{this} APPLY({expr})"
def grant_sql(self, expression: sqlglot.expressions.Grant) -> str:
4807    def grant_sql(self, expression: exp.Grant) -> str:
4808        privileges_sql = self.expressions(expression, key="privileges", flat=True)
4809
4810        kind = self.sql(expression, "kind")
4811        kind = f" {kind}" if kind else ""
4812
4813        securable = self.sql(expression, "securable")
4814        securable = f" {securable}" if securable else ""
4815
4816        principals = self.expressions(expression, key="principals", flat=True)
4817
4818        grant_option = " WITH GRANT OPTION" if expression.args.get("grant_option") else ""
4819
4820        return f"GRANT {privileges_sql} ON{kind}{securable} TO {principals}{grant_option}"
def grantprivilege_sql(self, expression: sqlglot.expressions.GrantPrivilege):
4822    def grantprivilege_sql(self, expression: exp.GrantPrivilege):
4823        this = self.sql(expression, "this")
4824        columns = self.expressions(expression, flat=True)
4825        columns = f"({columns})" if columns else ""
4826
4827        return f"{this}{columns}"
def grantprincipal_sql(self, expression: sqlglot.expressions.GrantPrincipal):
4829    def grantprincipal_sql(self, expression: exp.GrantPrincipal):
4830        this = self.sql(expression, "this")
4831
4832        kind = self.sql(expression, "kind")
4833        kind = f"{kind} " if kind else ""
4834
4835        return f"{kind}{this}"
def columns_sql(self, expression: sqlglot.expressions.Columns):
4837    def columns_sql(self, expression: exp.Columns):
4838        func = self.function_fallback_sql(expression)
4839        if expression.args.get("unpack"):
4840            func = f"*{func}"
4841
4842        return func
def overlay_sql(self, expression: sqlglot.expressions.Overlay):
4844    def overlay_sql(self, expression: exp.Overlay):
4845        this = self.sql(expression, "this")
4846        expr = self.sql(expression, "expression")
4847        from_sql = self.sql(expression, "from")
4848        for_sql = self.sql(expression, "for")
4849        for_sql = f" FOR {for_sql}" if for_sql else ""
4850
4851        return f"OVERLAY({this} PLACING {expr} FROM {from_sql}{for_sql})"
@unsupported_args('format')
def todouble_sql(self, expression: sqlglot.expressions.ToDouble) -> str:
4853    @unsupported_args("format")
4854    def todouble_sql(self, expression: exp.ToDouble) -> str:
4855        return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
def string_sql(self, expression: sqlglot.expressions.String) -> str:
4857    def string_sql(self, expression: exp.String) -> str:
4858        this = expression.this
4859        zone = expression.args.get("zone")
4860
4861        if zone:
4862            # This is a BigQuery specific argument for STRING(<timestamp_expr>, <time_zone>)
4863            # BigQuery stores timestamps internally as UTC, so ConvertTimezone is used with UTC
4864            # set for source_tz to transpile the time conversion before the STRING cast
4865            this = exp.ConvertTimezone(
4866                source_tz=exp.Literal.string("UTC"), target_tz=zone, timestamp=this
4867            )
4868
4869        return self.sql(exp.cast(this, exp.DataType.Type.VARCHAR))
def median_sql(self, expression: sqlglot.expressions.Median):
4871    def median_sql(self, expression: exp.Median):
4872        if not self.SUPPORTS_MEDIAN:
4873            return self.sql(
4874                exp.PercentileCont(this=expression.this, expression=exp.Literal.number(0.5))
4875            )
4876
4877        return self.function_fallback_sql(expression)
def overflowtruncatebehavior_sql(self, expression: sqlglot.expressions.OverflowTruncateBehavior) -> str:
4879    def overflowtruncatebehavior_sql(self, expression: exp.OverflowTruncateBehavior) -> str:
4880        filler = self.sql(expression, "this")
4881        filler = f" {filler}" if filler else ""
4882        with_count = "WITH COUNT" if expression.args.get("with_count") else "WITHOUT COUNT"
4883        return f"TRUNCATE{filler} {with_count}"
def unixseconds_sql(self, expression: sqlglot.expressions.UnixSeconds) -> str:
4885    def unixseconds_sql(self, expression: exp.UnixSeconds) -> str:
4886        if self.SUPPORTS_UNIX_SECONDS:
4887            return self.function_fallback_sql(expression)
4888
4889        start_ts = exp.cast(
4890            exp.Literal.string("1970-01-01 00:00:00+00"), to=exp.DataType.Type.TIMESTAMPTZ
4891        )
4892
4893        return self.sql(
4894            exp.TimestampDiff(this=expression.this, expression=start_ts, unit=exp.var("SECONDS"))
4895        )
def arraysize_sql(self, expression: sqlglot.expressions.ArraySize) -> str:
4897    def arraysize_sql(self, expression: exp.ArraySize) -> str:
4898        dim = expression.expression
4899
4900        # For dialects that don't support the dimension arg, we can safely transpile it's default value (1st dimension)
4901        if dim and self.ARRAY_SIZE_DIM_REQUIRED is None:
4902            if not (dim.is_int and dim.name == "1"):
4903                self.unsupported("Cannot transpile dimension argument for ARRAY_LENGTH")
4904            dim = None
4905
4906        # If dimension is required but not specified, default initialize it
4907        if self.ARRAY_SIZE_DIM_REQUIRED and not dim:
4908            dim = exp.Literal.number(1)
4909
4910        return self.func(self.ARRAY_SIZE_NAME, expression.this, dim)
def attach_sql(self, expression: sqlglot.expressions.Attach) -> str:
4912    def attach_sql(self, expression: exp.Attach) -> str:
4913        this = self.sql(expression, "this")
4914        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
4915        expressions = self.expressions(expression)
4916        expressions = f" ({expressions})" if expressions else ""
4917
4918        return f"ATTACH{exists_sql} {this}{expressions}"
def detach_sql(self, expression: sqlglot.expressions.Detach) -> str:
4920    def detach_sql(self, expression: exp.Detach) -> str:
4921        this = self.sql(expression, "this")
4922        # the DATABASE keyword is required if IF EXISTS is set
4923        # without it, DuckDB throws an error: Parser Error: syntax error at or near "exists" (Line Number: 1)
4924        # ref: https://duckdb.org/docs/stable/sql/statements/attach.html#detach-syntax
4925        exists_sql = " DATABASE IF EXISTS" if expression.args.get("exists") else ""
4926
4927        return f"DETACH{exists_sql} {this}"
def attachoption_sql(self, expression: sqlglot.expressions.AttachOption) -> str:
4929    def attachoption_sql(self, expression: exp.AttachOption) -> str:
4930        this = self.sql(expression, "this")
4931        value = self.sql(expression, "expression")
4932        value = f" {value}" if value else ""
4933        return f"{this}{value}"
def watermarkcolumnconstraint_sql(self, expression: sqlglot.expressions.WatermarkColumnConstraint) -> str:
4935    def watermarkcolumnconstraint_sql(self, expression: exp.WatermarkColumnConstraint) -> str:
4936        return (
4937            f"WATERMARK FOR {self.sql(expression, 'this')} AS {self.sql(expression, 'expression')}"
4938        )
def encodeproperty_sql(self, expression: sqlglot.expressions.EncodeProperty) -> str:
4940    def encodeproperty_sql(self, expression: exp.EncodeProperty) -> str:
4941        encode = "KEY ENCODE" if expression.args.get("key") else "ENCODE"
4942        encode = f"{encode} {self.sql(expression, 'this')}"
4943
4944        properties = expression.args.get("properties")
4945        if properties:
4946            encode = f"{encode} {self.properties(properties)}"
4947
4948        return encode
def includeproperty_sql(self, expression: sqlglot.expressions.IncludeProperty) -> str:
4950    def includeproperty_sql(self, expression: exp.IncludeProperty) -> str:
4951        this = self.sql(expression, "this")
4952        include = f"INCLUDE {this}"
4953
4954        column_def = self.sql(expression, "column_def")
4955        if column_def:
4956            include = f"{include} {column_def}"
4957
4958        alias = self.sql(expression, "alias")
4959        if alias:
4960            include = f"{include} AS {alias}"
4961
4962        return include
def xmlelement_sql(self, expression: sqlglot.expressions.XMLElement) -> str:
4964    def xmlelement_sql(self, expression: exp.XMLElement) -> str:
4965        name = f"NAME {self.sql(expression, 'this')}"
4966        return self.func("XMLELEMENT", name, *expression.expressions)
def xmlkeyvalueoption_sql(self, expression: sqlglot.expressions.XMLKeyValueOption) -> str:
4968    def xmlkeyvalueoption_sql(self, expression: exp.XMLKeyValueOption) -> str:
4969        this = self.sql(expression, "this")
4970        expr = self.sql(expression, "expression")
4971        expr = f"({expr})" if expr else ""
4972        return f"{this}{expr}"
def partitionbyrangeproperty_sql(self, expression: sqlglot.expressions.PartitionByRangeProperty) -> str:
4974    def partitionbyrangeproperty_sql(self, expression: exp.PartitionByRangeProperty) -> str:
4975        partitions = self.expressions(expression, "partition_expressions")
4976        create = self.expressions(expression, "create_expressions")
4977        return f"PARTITION BY RANGE {self.wrap(partitions)} {self.wrap(create)}"
def partitionbyrangepropertydynamic_sql( self, expression: sqlglot.expressions.PartitionByRangePropertyDynamic) -> str:
4979    def partitionbyrangepropertydynamic_sql(
4980        self, expression: exp.PartitionByRangePropertyDynamic
4981    ) -> str:
4982        start = self.sql(expression, "start")
4983        end = self.sql(expression, "end")
4984
4985        every = expression.args["every"]
4986        if isinstance(every, exp.Interval) and every.this.is_string:
4987            every.this.replace(exp.Literal.number(every.name))
4988
4989        return f"START {self.wrap(start)} END {self.wrap(end)} EVERY {self.wrap(self.sql(every))}"
def unpivotcolumns_sql(self, expression: sqlglot.expressions.UnpivotColumns) -> str:
4991    def unpivotcolumns_sql(self, expression: exp.UnpivotColumns) -> str:
4992        name = self.sql(expression, "this")
4993        values = self.expressions(expression, flat=True)
4994
4995        return f"NAME {name} VALUE {values}"
def analyzesample_sql(self, expression: sqlglot.expressions.AnalyzeSample) -> str:
4997    def analyzesample_sql(self, expression: exp.AnalyzeSample) -> str:
4998        kind = self.sql(expression, "kind")
4999        sample = self.sql(expression, "sample")
5000        return f"SAMPLE {sample} {kind}"
def analyzestatistics_sql(self, expression: sqlglot.expressions.AnalyzeStatistics) -> str:
5002    def analyzestatistics_sql(self, expression: exp.AnalyzeStatistics) -> str:
5003        kind = self.sql(expression, "kind")
5004        option = self.sql(expression, "option")
5005        option = f" {option}" if option else ""
5006        this = self.sql(expression, "this")
5007        this = f" {this}" if this else ""
5008        columns = self.expressions(expression)
5009        columns = f" {columns}" if columns else ""
5010        return f"{kind}{option} STATISTICS{this}{columns}"
def analyzehistogram_sql(self, expression: sqlglot.expressions.AnalyzeHistogram) -> str:
5012    def analyzehistogram_sql(self, expression: exp.AnalyzeHistogram) -> str:
5013        this = self.sql(expression, "this")
5014        columns = self.expressions(expression)
5015        inner_expression = self.sql(expression, "expression")
5016        inner_expression = f" {inner_expression}" if inner_expression else ""
5017        update_options = self.sql(expression, "update_options")
5018        update_options = f" {update_options} UPDATE" if update_options else ""
5019        return f"{this} HISTOGRAM ON {columns}{inner_expression}{update_options}"
def analyzedelete_sql(self, expression: sqlglot.expressions.AnalyzeDelete) -> str:
5021    def analyzedelete_sql(self, expression: exp.AnalyzeDelete) -> str:
5022        kind = self.sql(expression, "kind")
5023        kind = f" {kind}" if kind else ""
5024        return f"DELETE{kind} STATISTICS"
def analyzelistchainedrows_sql(self, expression: sqlglot.expressions.AnalyzeListChainedRows) -> str:
5026    def analyzelistchainedrows_sql(self, expression: exp.AnalyzeListChainedRows) -> str:
5027        inner_expression = self.sql(expression, "expression")
5028        return f"LIST CHAINED ROWS{inner_expression}"
def analyzevalidate_sql(self, expression: sqlglot.expressions.AnalyzeValidate) -> str:
5030    def analyzevalidate_sql(self, expression: exp.AnalyzeValidate) -> str:
5031        kind = self.sql(expression, "kind")
5032        this = self.sql(expression, "this")
5033        this = f" {this}" if this else ""
5034        inner_expression = self.sql(expression, "expression")
5035        return f"VALIDATE {kind}{this}{inner_expression}"
def analyze_sql(self, expression: sqlglot.expressions.Analyze) -> str:
5037    def analyze_sql(self, expression: exp.Analyze) -> str:
5038        options = self.expressions(expression, key="options", sep=" ")
5039        options = f" {options}" if options else ""
5040        kind = self.sql(expression, "kind")
5041        kind = f" {kind}" if kind else ""
5042        this = self.sql(expression, "this")
5043        this = f" {this}" if this else ""
5044        mode = self.sql(expression, "mode")
5045        mode = f" {mode}" if mode else ""
5046        properties = self.sql(expression, "properties")
5047        properties = f" {properties}" if properties else ""
5048        partition = self.sql(expression, "partition")
5049        partition = f" {partition}" if partition else ""
5050        inner_expression = self.sql(expression, "expression")
5051        inner_expression = f" {inner_expression}" if inner_expression else ""
5052        return f"ANALYZE{options}{kind}{this}{partition}{mode}{inner_expression}{properties}"
def xmltable_sql(self, expression: sqlglot.expressions.XMLTable) -> str:
5054    def xmltable_sql(self, expression: exp.XMLTable) -> str:
5055        this = self.sql(expression, "this")
5056        namespaces = self.expressions(expression, key="namespaces")
5057        namespaces = f"XMLNAMESPACES({namespaces}), " if namespaces else ""
5058        passing = self.expressions(expression, key="passing")
5059        passing = f"{self.sep()}PASSING{self.seg(passing)}" if passing else ""
5060        columns = self.expressions(expression, key="columns")
5061        columns = f"{self.sep()}COLUMNS{self.seg(columns)}" if columns else ""
5062        by_ref = f"{self.sep()}RETURNING SEQUENCE BY REF" if expression.args.get("by_ref") else ""
5063        return f"XMLTABLE({self.sep('')}{self.indent(namespaces + this + passing + by_ref + columns)}{self.seg(')', sep='')}"
def xmlnamespace_sql(self, expression: sqlglot.expressions.XMLNamespace) -> str:
5065    def xmlnamespace_sql(self, expression: exp.XMLNamespace) -> str:
5066        this = self.sql(expression, "this")
5067        return this if isinstance(expression.this, exp.Alias) else f"DEFAULT {this}"
def export_sql(self, expression: sqlglot.expressions.Export) -> str:
5069    def export_sql(self, expression: exp.Export) -> str:
5070        this = self.sql(expression, "this")
5071        connection = self.sql(expression, "connection")
5072        connection = f"WITH CONNECTION {connection} " if connection else ""
5073        options = self.sql(expression, "options")
5074        return f"EXPORT DATA {connection}{options} AS {this}"
def declare_sql(self, expression: sqlglot.expressions.Declare) -> str:
5076    def declare_sql(self, expression: exp.Declare) -> str:
5077        return f"DECLARE {self.expressions(expression, flat=True)}"
def declareitem_sql(self, expression: sqlglot.expressions.DeclareItem) -> str:
5079    def declareitem_sql(self, expression: exp.DeclareItem) -> str:
5080        variable = self.sql(expression, "this")
5081        default = self.sql(expression, "default")
5082        default = f" = {default}" if default else ""
5083
5084        kind = self.sql(expression, "kind")
5085        if isinstance(expression.args.get("kind"), exp.Schema):
5086            kind = f"TABLE {kind}"
5087
5088        return f"{variable} AS {kind}{default}"
def recursivewithsearch_sql(self, expression: sqlglot.expressions.RecursiveWithSearch) -> str:
5090    def recursivewithsearch_sql(self, expression: exp.RecursiveWithSearch) -> str:
5091        kind = self.sql(expression, "kind")
5092        this = self.sql(expression, "this")
5093        set = self.sql(expression, "expression")
5094        using = self.sql(expression, "using")
5095        using = f" USING {using}" if using else ""
5096
5097        kind_sql = kind if kind == "CYCLE" else f"SEARCH {kind} FIRST BY"
5098
5099        return f"{kind_sql} {this} SET {set}{using}"
def parameterizedagg_sql(self, expression: sqlglot.expressions.ParameterizedAgg) -> str:
5101    def parameterizedagg_sql(self, expression: exp.ParameterizedAgg) -> str:
5102        params = self.expressions(expression, key="params", flat=True)
5103        return self.func(expression.name, *expression.expressions) + f"({params})"
def anonymousaggfunc_sql(self, expression: sqlglot.expressions.AnonymousAggFunc) -> str:
5105    def anonymousaggfunc_sql(self, expression: exp.AnonymousAggFunc) -> str:
5106        return self.func(expression.name, *expression.expressions)
def combinedaggfunc_sql(self, expression: sqlglot.expressions.CombinedAggFunc) -> str:
5108    def combinedaggfunc_sql(self, expression: exp.CombinedAggFunc) -> str:
5109        return self.anonymousaggfunc_sql(expression)
def combinedparameterizedagg_sql(self, expression: sqlglot.expressions.CombinedParameterizedAgg) -> str:
5111    def combinedparameterizedagg_sql(self, expression: exp.CombinedParameterizedAgg) -> str:
5112        return self.parameterizedagg_sql(expression)
def show_sql(self, expression: sqlglot.expressions.Show) -> str:
5114    def show_sql(self, expression: exp.Show) -> str:
5115        self.unsupported("Unsupported SHOW statement")
5116        return ""
def get_put_sql( self, expression: sqlglot.expressions.Put | sqlglot.expressions.Get) -> str:
5118    def get_put_sql(self, expression: exp.Put | exp.Get) -> str:
5119        # Snowflake GET/PUT statements:
5120        #   PUT <file> <internalStage> <properties>
5121        #   GET <internalStage> <file> <properties>
5122        props = expression.args.get("properties")
5123        props_sql = self.properties(props, prefix=" ", sep=" ", wrapped=False) if props else ""
5124        this = self.sql(expression, "this")
5125        target = self.sql(expression, "target")
5126
5127        if isinstance(expression, exp.Put):
5128            return f"PUT {this} {target}{props_sql}"
5129        else:
5130            return f"GET {target} {this}{props_sql}"
def translatecharacters_sql(self, expression: sqlglot.expressions.TranslateCharacters):
5132    def translatecharacters_sql(self, expression: exp.TranslateCharacters):
5133        this = self.sql(expression, "this")
5134        expr = self.sql(expression, "expression")
5135        with_error = " WITH ERROR" if expression.args.get("with_error") else ""
5136        return f"TRANSLATE({this} USING {expr}{with_error})"
def decodecase_sql(self, expression: sqlglot.expressions.DecodeCase) -> str:
5138    def decodecase_sql(self, expression: exp.DecodeCase) -> str:
5139        if self.SUPPORTS_DECODE_CASE:
5140            return self.func("DECODE", *expression.expressions)
5141
5142        expression, *expressions = expression.expressions
5143
5144        ifs = []
5145        for search, result in zip(expressions[::2], expressions[1::2]):
5146            if isinstance(search, exp.Literal):
5147                ifs.append(exp.If(this=expression.eq(search), true=result))
5148            elif isinstance(search, exp.Null):
5149                ifs.append(exp.If(this=expression.is_(exp.Null()), true=result))
5150            else:
5151                if isinstance(search, exp.Binary):
5152                    search = exp.paren(search)
5153
5154                cond = exp.or_(
5155                    expression.eq(search),
5156                    exp.and_(expression.is_(exp.Null()), search.is_(exp.Null()), copy=False),
5157                    copy=False,
5158                )
5159                ifs.append(exp.If(this=cond, true=result))
5160
5161        case = exp.Case(ifs=ifs, default=expressions[-1] if len(expressions) % 2 == 1 else None)
5162        return self.sql(case)
def semanticview_sql(self, expression: sqlglot.expressions.SemanticView) -> str:
5164    def semanticview_sql(self, expression: exp.SemanticView) -> str:
5165        this = self.sql(expression, "this")
5166        this = self.seg(this, sep="")
5167        dimensions = self.expressions(
5168            expression, "dimensions", dynamic=True, skip_first=True, skip_last=True
5169        )
5170        dimensions = self.seg(f"DIMENSIONS {dimensions}") if dimensions else ""
5171        metrics = self.expressions(
5172            expression, "metrics", dynamic=True, skip_first=True, skip_last=True
5173        )
5174        metrics = self.seg(f"METRICS {metrics}") if metrics else ""
5175        where = self.sql(expression, "where")
5176        where = self.seg(f"WHERE {where}") if where else ""
5177        return f"SEMANTIC_VIEW({self.indent(this + metrics + dimensions + where)}{self.seg(')', sep='')}"
def getextract_sql(self, expression: sqlglot.expressions.GetExtract) -> str:
5179    def getextract_sql(self, expression: exp.GetExtract) -> str:
5180        this = expression.this
5181        expr = expression.expression
5182
5183        if not this.type or not expression.type:
5184            from sqlglot.optimizer.annotate_types import annotate_types
5185
5186            this = annotate_types(this, dialect=self.dialect)
5187
5188        if this.is_type(*(exp.DataType.Type.ARRAY, exp.DataType.Type.MAP)):
5189            return self.sql(exp.Bracket(this=this, expressions=[expr]))
5190
5191        return self.sql(exp.JSONExtract(this=this, expression=self.dialect.to_json_path(expr)))
def datefromunixdate_sql(self, expression: sqlglot.expressions.DateFromUnixDate) -> str:
5193    def datefromunixdate_sql(self, expression: exp.DateFromUnixDate) -> str:
5194        return self.sql(
5195            exp.DateAdd(
5196                this=exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
5197                expression=expression.this,
5198                unit=exp.var("DAY"),
5199            )
5200        )
def space_sql( self: Generator, expression: sqlglot.expressions.Space) -> str:
5202    def space_sql(self: Generator, expression: exp.Space) -> str:
5203        return self.sql(exp.Repeat(this=exp.Literal.string(" "), times=expression.this))
def buildproperty_sql(self, expression: sqlglot.expressions.BuildProperty) -> str:
5205    def buildproperty_sql(self, expression: exp.BuildProperty) -> str:
5206        return f"BUILD {self.sql(expression, 'this')}"
def refreshtriggerproperty_sql(self, expression: sqlglot.expressions.RefreshTriggerProperty) -> str:
5208    def refreshtriggerproperty_sql(self, expression: exp.RefreshTriggerProperty) -> str:
5209        method = self.sql(expression, "method")
5210        kind = expression.args.get("kind")
5211        if not kind:
5212            return f"REFRESH {method}"
5213
5214        every = self.sql(expression, "every")
5215        unit = self.sql(expression, "unit")
5216        every = f" EVERY {every} {unit}" if every else ""
5217        starts = self.sql(expression, "starts")
5218        starts = f" STARTS {starts}" if starts else ""
5219
5220        return f"REFRESH {method} ON {kind}{every}{starts}"